Modelo

source("../../lib/som-utils.R")

Attaching package: 'dplyr'
The following objects are masked from 'package:stats':

    filter, lag
The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
source("../../lib/maps-utils.R")
Linking to GEOS 3.8.0, GDAL 3.0.4, PROJ 6.3.1

Carga del modelo desde disco

mpr.set_base_path_analysis()
model <- mpr.load_model("som-345.rds.xz")
summary(model)
SOM of size 5x5 with a hexagonal topology and a bubble neighbourhood function.
The number of data layers is 1.
Distance measure(s) used: sumofsquares.
Training data included: 94881 objects.
Mean distance to the closest unit in the map: 0.083.
plot(model, type="changes")

Carga del dataset de entrada

df <- mpr.load_data("datos_mes.csv.xz")
df
summary(df)
 id_estacion           fecha             fecha_cnt           tmax      
 Length:94881       Length:94881       Min.   : 1.000   Min.   :-53.0  
 Class :character   Class :character   1st Qu.: 4.000   1st Qu.:148.0  
 Mode  :character   Mode  :character   Median : 6.000   Median :198.0  
                                       Mean   : 6.497   Mean   :200.2  
                                       3rd Qu.: 9.000   3rd Qu.:255.0  
                                       Max.   :12.000   Max.   :403.0  
      tmin             precip           nevada           prof_nieve      
 Min.   :-121.00   Min.   :  0.00   Min.   :0.000000   Min.   :   0.000  
 1st Qu.:  53.00   1st Qu.:  3.00   1st Qu.:0.000000   1st Qu.:   0.000  
 Median :  98.00   Median : 10.00   Median :0.000000   Median :   0.000  
 Mean   :  98.86   Mean   : 16.25   Mean   :0.000295   Mean   :   0.467  
 3rd Qu.: 148.00   3rd Qu.: 22.00   3rd Qu.:0.000000   3rd Qu.:   0.000  
 Max.   : 254.00   Max.   :422.00   Max.   :6.000000   Max.   :1834.000  
    longitud        latitud            altitud      
 Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.:38.28   1st Qu.: -5.6417   1st Qu.:  42.0  
 Median :40.82   Median : -3.4500   Median : 247.0  
 Mean   :39.66   Mean   : -3.4350   Mean   : 418.5  
 3rd Qu.:42.08   3rd Qu.:  0.4914   3rd Qu.: 656.0  
 Max.   :43.57   Max.   :  4.2156   Max.   :2535.0  

Carga de los mapas

world <- ne_countries(scale = "medium", returnclass = "sf")
spain <- subset(world, admin == "Spain")

Mapa de densidad

plot(model, type="count", shape = "straight", palette.name = mpr.degrade.bleu)

NĂºmero de elementos en cada celda:

nb <- table(model$unit.classif)
print(nb)

   1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16 
4543 1717 1078  298   61 6971 3378 3867 1565  323 6693 5036 4079 2519 1040 5012 
  17   18   19   20   21   22   23   24   25 
3322 2275 4601 4309 7402 5844 5407 7053 6488 

ComprobaciĂ³n de nodos vacĂ­os:

dim_model <- 5*5;
len_nb = length(nb);
empty_nodes <- dim_model != len_nb;
if (empty_nodes) {
  print(paste("[Warning] Existen nodos vacĂ­os: ", len_nb, "/", dim_model))
}

Mapa de distancia entre vecinos

plot(model, type="dist.neighbours", shape = "straight")

Influencia de las variables

model_colnames = c("fecha_cnt", "precip")
model_ncol = length(model_colnames)

Mapa de variables.

plot(model, shape = "straight")

Mapa de calor por variable

par(mfrow=c(3,4))
for (j in 1:model_ncol) {
  plot(model, type="property", property=getCodes(model,1)[,j],
    palette.name=mpr.coolBlueHotRed,
    main=model_colnames[j],
    cex=0.5, shape = "straight")
}

CorrelaciĂ³n para cada columna del vector de nodos

if (!empty_nodes) {
  cor <- apply(getCodes(model,1), 2, mpr.weighted.correlation, w=nb, som=model)
  print(cor)
}
     fecha_cnt     precip
[1,] 0.8269473  0.2466224
[2,] 0.6635539 -0.5127185

RepresentaciĂ³n de cada variable en un mapa de factores:

if (!empty_nodes) {
  par(mfrow=c(1,1))
  plot(cor[1,], cor[2,], xlim=c(-1,1), ylim=c(-1,1), type="n")
  lines(c(-1,1),c(0,0))
  lines(c(0,0),c(-1,1))
  text(cor[1,], cor[2,], labels=model_colnames, cex=0.75)
  symbols(0,0,circles=1,inches=F,add=T)
}

Importancia de cada variable - varianza ponderada por el tamaño de la celda:

if (!empty_nodes) {
  sigma2 <- sqrt(apply(getCodes(model,1),2,function(x,effectif)
     {m<-sum(effectif*(x-weighted.mean(x,effectif))^2)/(sum(effectif)-1)},
     effectif=nb))
  print(sort(sigma2,decreasing=T))
}
fecha_cnt    precip 
0.9902213 0.9753982 

Clustering

if (!empty_nodes) {
  hac <- mpr.hac(model, nb)
}

VisualizaciĂ³n de 3 clĂºsteres:

if (!empty_nodes) {
  plot(hac, hang=-1, labels=F)
  rect.hclust(hac, k=3)
}

VisualizaciĂ³n de los clĂºsters en el mapa

A quĂ© clĂºster pertenece cada nodo del mapa de kohonen:

if (!empty_nodes) {
  groups <- cutree(hac, k=3)
  plot(model, type="mapping",
    bgcol=c("steelblue1","sienna1","yellowgreen","red","blue","yellow","purple","green","white","#1f77b4", '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2')[groups],
    shape = "straight", labels = "")
  add.cluster.boundaries(model, clustering=groups)
}

AnĂ¡lisis de las observaciones de cada cluster

if (!empty_nodes) {
  # Asignamos a cada registro su clĂºster
  df$cluster <- groups[model$unit.classif]
}

Nuevos dataframes por cluster

if (!empty_nodes) {
  # Creo nuevos dataframes, uno por cada clĂºster.
  df.cluster01 <- subset(df, cluster==1)
  df.cluster02 <- subset(df, cluster==2)
  df.cluster03 <- subset(df, cluster==3)

  # Extraigo del dataframe las features.
  df.cluster01 <- select(df.cluster01, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster02 <- select(df.cluster02, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster03 <- select(df.cluster03, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
}
if (!empty_nodes) summary(df.cluster01)
   fecha_cnt           tmax            tmin             precip     
 Min.   : 1.000   Min.   :-53.0   Min.   :-121.00   Min.   : 0.00  
 1st Qu.: 3.000   1st Qu.:149.0   1st Qu.:  52.00   1st Qu.: 3.00  
 Median : 6.000   Median :199.0   Median :  98.00   Median :10.00  
 Mean   : 6.448   Mean   :201.1   Mean   :  99.12   Mean   :14.67  
 3rd Qu.: 9.000   3rd Qu.:256.0   3rd Qu.: 148.00   3rd Qu.:21.00  
 Max.   :12.000   Max.   :403.0   Max.   : 254.00   Max.   :95.00  
     nevada           prof_nieve           longitud        latitud        
 Min.   :0.000000   Min.   :   0.0000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.000000   1st Qu.:   0.0000   1st Qu.:38.00   1st Qu.: -5.6417  
 Median :0.000000   Median :   0.0000   Median :40.82   Median : -3.1742  
 Mean   :0.000301   Mean   :   0.4281   Mean   :39.63   Mean   : -3.4105  
 3rd Qu.:0.000000   3rd Qu.:   0.0000   3rd Qu.:42.05   3rd Qu.:  0.4914  
 Max.   :6.000000   Max.   :1834.0000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  42.0  
 Median : 247.0  
 Mean   : 419.4  
 3rd Qu.: 656.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster02)
   fecha_cnt           tmax            tmin             precip      
 Min.   : 1.000   Min.   :-42.0   Min.   :-108.00   Min.   : 69.00  
 1st Qu.: 9.000   1st Qu.:123.0   1st Qu.:  58.00   1st Qu.: 78.00  
 Median :11.000   Median :152.0   Median :  85.00   Median : 92.00  
 Mean   : 9.227   Mean   :153.7   Mean   :  84.87   Mean   : 97.12  
 3rd Qu.:11.000   3rd Qu.:189.0   3rd Qu.: 115.00   3rd Qu.:110.00  
 Max.   :12.000   Max.   :336.0   Max.   : 219.00   Max.   :177.00  
     nevada    prof_nieve         longitud        latitud       
 Min.   :0   Min.   :  0.000   Min.   :27.82   Min.   :-17.889  
 1st Qu.:0   1st Qu.:  0.000   1st Qu.:40.78   1st Qu.: -8.411  
 Median :0   Median :  0.000   Median :42.43   Median : -4.488  
 Mean   :0   Mean   :  2.297   Mean   :41.23   Mean   : -4.677  
 3rd Qu.:0   3rd Qu.:  0.000   3rd Qu.:43.31   3rd Qu.: -1.787  
 Max.   :0   Max.   :892.000   Max.   :43.57   Max.   :  4.216  
    altitud      
 Min.   :   1.0  
 1st Qu.:  32.0  
 Median : 108.0  
 Mean   : 372.9  
 3rd Qu.: 370.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster03)
   fecha_cnt           tmax            tmin            precip          nevada 
 Min.   : 1.000   Min.   : 11.0   Min.   :-27.00   Min.   :173.0   Min.   :0  
 1st Qu.: 2.000   1st Qu.:115.0   1st Qu.: 53.00   1st Qu.:185.0   1st Qu.:0  
 Median : 6.000   Median :136.0   Median : 80.00   Median :205.0   Median :0  
 Mean   : 6.328   Mean   :151.5   Mean   : 84.85   Mean   :219.4   Mean   :0  
 3rd Qu.:11.000   3rd Qu.:158.0   3rd Qu.: 97.00   3rd Qu.:231.0   3rd Qu.:0  
 Max.   :12.000   Max.   :350.0   Max.   :223.00   Max.   :422.0   Max.   :0  
   prof_nieve     longitud        latitud           altitud      
 Min.   :  0   Min.   :28.31   Min.   :-16.499   Min.   :   4.0  
 1st Qu.:  0   1st Qu.:40.66   1st Qu.: -8.624   1st Qu.:  82.0  
 Median :  0   Median :42.24   Median : -8.411   Median : 261.0  
 Mean   : 10   Mean   :40.75   Mean   : -7.011   Mean   : 411.3  
 3rd Qu.:  0   3rd Qu.:42.89   3rd Qu.: -5.600   3rd Qu.: 370.0  
 Max.   :607   Max.   :43.36   Max.   :  2.825   Max.   :2371.0  

NĂºmero de elementos en cada clĂºster

if (!empty_nodes) {
  df.clusters.dim <- c(dim(df.cluster01)[1], dim(df.cluster02)[1], dim(df.cluster03)[1])
  barplot(df.clusters.dim,
          names.arg = c("cluster01", "cluster02", "cluster03"),
          col = "steelblue1")
}

DistribuciĂ³n de los datos

if (!empty_nodes) mpr.hist(df.cluster01)

if (!empty_nodes) mpr.hist(df.cluster02)

if (!empty_nodes) mpr.hist(df.cluster03)

if (!empty_nodes) mpr.boxplot(df.cluster01)
if (!empty_nodes) mpr.boxplot(df.cluster02)

if (!empty_nodes) mpr.boxplot(df.cluster03)

LocalizaciĂ³n geogrĂ¡fica de las estaciones de medida de cada cluster

# Agrupa por longitud y latitud para rellenar el mapa con menos datos.
if (!empty_nodes) {
  df.cluster01.grouped <- mpr.group_by_geo(df.cluster01)
  df.cluster02.grouped <- mpr.group_by_geo(df.cluster02)
  df.cluster03.grouped <- mpr.group_by_geo(df.cluster03)
}
if (!empty_nodes) mpr.draw_map(spain, df.cluster01.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster02.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster03.grouped)

VisualizaciĂ³n de 4 clĂºsteres:

if (!empty_nodes) {
  plot(hac, hang=-1, labels=F)
  rect.hclust(hac, k=4)
}

VisualizaciĂ³n de los clĂºsters en el mapa

A quĂ© clĂºster pertenece cada nodo del mapa de kohonen:

if (!empty_nodes) {
  groups <- cutree(hac, k=4)
  plot(model, type="mapping",
    bgcol=c("steelblue1","sienna1","yellowgreen","red","blue","yellow","purple","green","white","#1f77b4", '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2')[groups],
    shape = "straight", labels = "")
  add.cluster.boundaries(model, clustering=groups)
}

AnĂ¡lisis de las observaciones de cada cluster

if (!empty_nodes) {
  # Asignamos a cada registro su clĂºster
  df$cluster <- groups[model$unit.classif]
}

Nuevos dataframes por cluster

if (!empty_nodes) {
  # Creo nuevos dataframes, uno por cada clĂºster.
  df.cluster01 <- subset(df, cluster==1)
  df.cluster02 <- subset(df, cluster==2)
  df.cluster03 <- subset(df, cluster==3)
  df.cluster04 <- subset(df, cluster==4)

  # Extraigo del dataframe las features.
  df.cluster01 <- select(df.cluster01, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster02 <- select(df.cluster02, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster03 <- select(df.cluster03, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster04 <- select(df.cluster04, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
}
if (!empty_nodes) summary(df.cluster01)
   fecha_cnt          tmax            tmin             precip     
 Min.   :1.000   Min.   :-53.0   Min.   :-121.00   Min.   : 0.00  
 1st Qu.:2.000   1st Qu.:132.0   1st Qu.:  32.00   1st Qu.: 6.00  
 Median :3.000   Median :166.0   Median :  67.00   Median :14.00  
 Mean   :3.138   Mean   :165.2   Mean   :  66.55   Mean   :18.36  
 3rd Qu.:4.000   3rd Qu.:201.0   3rd Qu.: 100.00   3rd Qu.:26.00  
 Max.   :7.000   Max.   :352.0   Max.   : 213.00   Max.   :95.00  
     nevada           prof_nieve           longitud        latitud        
 Min.   :0.000000   Min.   :   0.0000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.000000   1st Qu.:   0.0000   1st Qu.:38.88   1st Qu.: -5.6156  
 Median :0.000000   Median :   0.0000   Median :40.96   Median : -2.9553  
 Mean   :0.000477   Mean   :   0.8945   Mean   :40.00   Mean   : -3.2067  
 3rd Qu.:0.000000   3rd Qu.:   0.0000   3rd Qu.:42.24   3rd Qu.:  0.4914  
 Max.   :6.000000   Max.   :1834.0000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  44.0  
 Median : 258.0  
 Mean   : 435.1  
 3rd Qu.: 667.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster02)
   fecha_cnt           tmax            tmin             precip      
 Min.   : 1.000   Min.   :-42.0   Min.   :-108.00   Min.   : 69.00  
 1st Qu.: 9.000   1st Qu.:123.0   1st Qu.:  58.00   1st Qu.: 78.00  
 Median :11.000   Median :152.0   Median :  85.00   Median : 92.00  
 Mean   : 9.227   Mean   :153.7   Mean   :  84.87   Mean   : 97.12  
 3rd Qu.:11.000   3rd Qu.:189.0   3rd Qu.: 115.00   3rd Qu.:110.00  
 Max.   :12.000   Max.   :336.0   Max.   : 219.00   Max.   :177.00  
     nevada    prof_nieve         longitud        latitud       
 Min.   :0   Min.   :  0.000   Min.   :27.82   Min.   :-17.889  
 1st Qu.:0   1st Qu.:  0.000   1st Qu.:40.78   1st Qu.: -8.411  
 Median :0   Median :  0.000   Median :42.43   Median : -4.488  
 Mean   :0   Mean   :  2.297   Mean   :41.23   Mean   : -4.677  
 3rd Qu.:0   3rd Qu.:  0.000   3rd Qu.:43.31   3rd Qu.: -1.787  
 Max.   :0   Max.   :892.000   Max.   :43.57   Max.   :  4.216  
    altitud      
 Min.   :   1.0  
 1st Qu.:  32.0  
 Median : 108.0  
 Mean   : 372.9  
 3rd Qu.: 370.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster03)
   fecha_cnt           tmax            tmin            precip          nevada 
 Min.   : 1.000   Min.   : 11.0   Min.   :-27.00   Min.   :173.0   Min.   :0  
 1st Qu.: 2.000   1st Qu.:115.0   1st Qu.: 53.00   1st Qu.:185.0   1st Qu.:0  
 Median : 6.000   Median :136.0   Median : 80.00   Median :205.0   Median :0  
 Mean   : 6.328   Mean   :151.5   Mean   : 84.85   Mean   :219.4   Mean   :0  
 3rd Qu.:11.000   3rd Qu.:158.0   3rd Qu.: 97.00   3rd Qu.:231.0   3rd Qu.:0  
 Max.   :12.000   Max.   :350.0   Max.   :223.00   Max.   :422.0   Max.   :0  
   prof_nieve     longitud        latitud           altitud      
 Min.   :  0   Min.   :28.31   Min.   :-16.499   Min.   :   4.0  
 1st Qu.:  0   1st Qu.:40.66   1st Qu.: -8.624   1st Qu.:  82.0  
 Median :  0   Median :42.24   Median : -8.411   Median : 261.0  
 Mean   : 10   Mean   :40.75   Mean   : -7.011   Mean   : 411.3  
 3rd Qu.:  0   3rd Qu.:42.89   3rd Qu.: -5.600   3rd Qu.: 370.0  
 Max.   :607   Max.   :43.36   Max.   :  2.825   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster04)
   fecha_cnt           tmax            tmin            precip     
 Min.   : 5.000   Min.   :-24.0   Min.   :-110.0   Min.   : 0.00  
 1st Qu.: 7.000   1st Qu.:177.0   1st Qu.:  81.0   1st Qu.: 1.00  
 Median : 9.000   Median :238.0   Median : 132.0   Median : 7.00  
 Mean   : 8.924   Mean   :227.9   Mean   : 123.5   Mean   :11.92  
 3rd Qu.:11.000   3rd Qu.:282.0   3rd Qu.: 170.0   3rd Qu.:17.00  
 Max.   :12.000   Max.   :403.0   Max.   : 254.0   Max.   :68.00  
     nevada            prof_nieve          longitud        latitud        
 Min.   :0.0000000   Min.   :  0.0000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.0000000   1st Qu.:  0.0000   1st Qu.:37.96   1st Qu.: -5.6492  
 Median :0.0000000   Median :  0.0000   Median :40.66   Median : -3.4503  
 Mean   :0.0001689   Mean   :  0.0793   Mean   :39.36   Mean   : -3.5630  
 3rd Qu.:0.0000000   3rd Qu.:  0.0000   3rd Qu.:41.84   3rd Qu.:  0.4914  
 Max.   :3.0000000   Max.   :338.0000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  35.0  
 Median : 192.0  
 Mean   : 407.6  
 3rd Qu.: 656.0  
 Max.   :2535.0  

NĂºmero de elementos en cada clĂºster

if (!empty_nodes) {
  df.clusters.dim <- c(dim(df.cluster01)[1], dim(df.cluster02)[1], dim(df.cluster03)[1], dim(df.cluster04)[1])
  barplot(df.clusters.dim,
          names.arg = c("cluster01", "cluster02", "cluster03", "cluster04"),
          col = "steelblue1")
}

DistribuciĂ³n de los datos

if (!empty_nodes) mpr.hist(df.cluster01)

if (!empty_nodes) mpr.hist(df.cluster02)

if (!empty_nodes) mpr.hist(df.cluster03)

if (!empty_nodes) mpr.hist(df.cluster04)

if (!empty_nodes) mpr.boxplot(df.cluster01)
if (!empty_nodes) mpr.boxplot(df.cluster02)

if (!empty_nodes) mpr.boxplot(df.cluster03)

if (!empty_nodes) mpr.boxplot(df.cluster04)

LocalizaciĂ³n geogrĂ¡fica de las estaciones de medida de cada cluster

# Agrupa por longitud y latitud para rellenar el mapa con menos datos.
if (!empty_nodes) {
  df.cluster01.grouped <- mpr.group_by_geo(df.cluster01)
  df.cluster02.grouped <- mpr.group_by_geo(df.cluster02)
  df.cluster03.grouped <- mpr.group_by_geo(df.cluster03)
  df.cluster04.grouped <- mpr.group_by_geo(df.cluster04)
}
if (!empty_nodes) mpr.draw_map(spain, df.cluster01.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster02.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster03.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster04.grouped)

VisualizaciĂ³n de 5 clĂºsteres:

if (!empty_nodes) {
  plot(hac, hang=-1, labels=F)
  rect.hclust(hac, k=5)
}

VisualizaciĂ³n de los clĂºsters en el mapa

A quĂ© clĂºster pertenece cada nodo del mapa de kohonen:

if (!empty_nodes) {
  groups <- cutree(hac, k=5)
  plot(model, type="mapping",
    bgcol=c("steelblue1","sienna1","yellowgreen","red","blue","yellow","purple","green","white","#1f77b4", '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2')[groups],
    shape = "straight", labels = "")
  add.cluster.boundaries(model, clustering=groups)
}

AnĂ¡lisis de las observaciones de cada cluster

if (!empty_nodes) {
  # Asignamos a cada registro su clĂºster
  df$cluster <- groups[model$unit.classif]
}

Nuevos dataframes por cluster

if (!empty_nodes) {
  # Creo nuevos dataframes, uno por cada clĂºster.
  df.cluster01 <- subset(df, cluster==1)
  df.cluster02 <- subset(df, cluster==2)
  df.cluster03 <- subset(df, cluster==3)
  df.cluster04 <- subset(df, cluster==4)
  df.cluster05 <- subset(df, cluster==5)

  # Extraigo del dataframe las features.
  df.cluster01 <- select(df.cluster01, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster02 <- select(df.cluster02, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster03 <- select(df.cluster03, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster04 <- select(df.cluster04, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster05 <- select(df.cluster05, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
}
if (!empty_nodes) summary(df.cluster01)
   fecha_cnt          tmax            tmin             precip     
 Min.   :1.000   Min.   :-53.0   Min.   :-121.00   Min.   : 0.00  
 1st Qu.:2.000   1st Qu.:134.0   1st Qu.:  31.00   1st Qu.: 6.00  
 Median :3.000   Median :169.0   Median :  66.00   Median :12.00  
 Mean   :3.128   Mean   :167.9   Mean   :  66.89   Mean   :13.92  
 3rd Qu.:4.000   3rd Qu.:204.0   3rd Qu.: 101.00   3rd Qu.:21.00  
 Max.   :6.000   Max.   :342.0   Max.   : 213.00   Max.   :39.00  
     nevada           prof_nieve           longitud        latitud        
 Min.   :0.000000   Min.   :   0.0000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.000000   1st Qu.:   0.0000   1st Qu.:38.88   1st Qu.: -5.5975  
 Median :0.000000   Median :   0.0000   Median :40.87   Median : -2.9056  
 Mean   :0.000535   Mean   :   0.4659   Mean   :39.82   Mean   : -3.1711  
 3rd Qu.:0.000000   3rd Qu.:   0.0000   3rd Qu.:42.00   3rd Qu.:  0.4914  
 Max.   :6.000000   Max.   :1494.0000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  44.0  
 Median : 263.0  
 Mean   : 425.4  
 3rd Qu.: 674.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster02)
   fecha_cnt          tmax            tmin             precip          nevada 
 Min.   :1.000   Min.   :-50.0   Min.   :-115.00   Min.   :39.00   Min.   :0  
 1st Qu.:2.000   1st Qu.:118.0   1st Qu.:  42.00   1st Qu.:44.00   1st Qu.:0  
 Median :3.000   Median :146.0   Median :  68.00   Median :50.00   Median :0  
 Mean   :3.219   Mean   :143.4   Mean   :  63.73   Mean   :54.52   Mean   :0  
 3rd Qu.:5.000   3rd Qu.:176.0   3rd Qu.:  93.00   3rd Qu.:62.00   3rd Qu.:0  
 Max.   :7.000   Max.   :352.0   Max.   : 210.00   Max.   :95.00   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   :   0.000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.:   0.000   1st Qu.:40.82   1st Qu.: -6.0556   1st Qu.:  47.0  
 Median :   0.000   Median :42.43   Median : -3.7994   Median : 251.0  
 Mean   :   4.385   Mean   :41.40   Mean   : -3.4961   Mean   : 514.5  
 3rd Qu.:   0.000   3rd Qu.:43.31   3rd Qu.:  0.4914   3rd Qu.: 582.0  
 Max.   :1834.000   Max.   :43.57   Max.   :  4.2156   Max.   :2535.0  
if (!empty_nodes) summary(df.cluster03)
   fecha_cnt           tmax            tmin             precip      
 Min.   : 1.000   Min.   :-42.0   Min.   :-108.00   Min.   : 69.00  
 1st Qu.: 9.000   1st Qu.:123.0   1st Qu.:  58.00   1st Qu.: 78.00  
 Median :11.000   Median :152.0   Median :  85.00   Median : 92.00  
 Mean   : 9.227   Mean   :153.7   Mean   :  84.87   Mean   : 97.12  
 3rd Qu.:11.000   3rd Qu.:189.0   3rd Qu.: 115.00   3rd Qu.:110.00  
 Max.   :12.000   Max.   :336.0   Max.   : 219.00   Max.   :177.00  
     nevada    prof_nieve         longitud        latitud       
 Min.   :0   Min.   :  0.000   Min.   :27.82   Min.   :-17.889  
 1st Qu.:0   1st Qu.:  0.000   1st Qu.:40.78   1st Qu.: -8.411  
 Median :0   Median :  0.000   Median :42.43   Median : -4.488  
 Mean   :0   Mean   :  2.297   Mean   :41.23   Mean   : -4.677  
 3rd Qu.:0   3rd Qu.:  0.000   3rd Qu.:43.31   3rd Qu.: -1.787  
 Max.   :0   Max.   :892.000   Max.   :43.57   Max.   :  4.216  
    altitud      
 Min.   :   1.0  
 1st Qu.:  32.0  
 Median : 108.0  
 Mean   : 372.9  
 3rd Qu.: 370.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster04)
   fecha_cnt           tmax            tmin            precip          nevada 
 Min.   : 1.000   Min.   : 11.0   Min.   :-27.00   Min.   :173.0   Min.   :0  
 1st Qu.: 2.000   1st Qu.:115.0   1st Qu.: 53.00   1st Qu.:185.0   1st Qu.:0  
 Median : 6.000   Median :136.0   Median : 80.00   Median :205.0   Median :0  
 Mean   : 6.328   Mean   :151.5   Mean   : 84.85   Mean   :219.4   Mean   :0  
 3rd Qu.:11.000   3rd Qu.:158.0   3rd Qu.: 97.00   3rd Qu.:231.0   3rd Qu.:0  
 Max.   :12.000   Max.   :350.0   Max.   :223.00   Max.   :422.0   Max.   :0  
   prof_nieve     longitud        latitud           altitud      
 Min.   :  0   Min.   :28.31   Min.   :-16.499   Min.   :   4.0  
 1st Qu.:  0   1st Qu.:40.66   1st Qu.: -8.624   1st Qu.:  82.0  
 Median :  0   Median :42.24   Median : -8.411   Median : 261.0  
 Mean   : 10   Mean   :40.75   Mean   : -7.011   Mean   : 411.3  
 3rd Qu.:  0   3rd Qu.:42.89   3rd Qu.: -5.600   3rd Qu.: 370.0  
 Max.   :607   Max.   :43.36   Max.   :  2.825   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster05)
   fecha_cnt           tmax            tmin            precip     
 Min.   : 5.000   Min.   :-24.0   Min.   :-110.0   Min.   : 0.00  
 1st Qu.: 7.000   1st Qu.:177.0   1st Qu.:  81.0   1st Qu.: 1.00  
 Median : 9.000   Median :238.0   Median : 132.0   Median : 7.00  
 Mean   : 8.924   Mean   :227.9   Mean   : 123.5   Mean   :11.92  
 3rd Qu.:11.000   3rd Qu.:282.0   3rd Qu.: 170.0   3rd Qu.:17.00  
 Max.   :12.000   Max.   :403.0   Max.   : 254.0   Max.   :68.00  
     nevada            prof_nieve          longitud        latitud        
 Min.   :0.0000000   Min.   :  0.0000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.0000000   1st Qu.:  0.0000   1st Qu.:37.96   1st Qu.: -5.6492  
 Median :0.0000000   Median :  0.0000   Median :40.66   Median : -3.4503  
 Mean   :0.0001689   Mean   :  0.0793   Mean   :39.36   Mean   : -3.5630  
 3rd Qu.:0.0000000   3rd Qu.:  0.0000   3rd Qu.:41.84   3rd Qu.:  0.4914  
 Max.   :3.0000000   Max.   :338.0000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  35.0  
 Median : 192.0  
 Mean   : 407.6  
 3rd Qu.: 656.0  
 Max.   :2535.0  

NĂºmero de elementos en cada clĂºster

if (!empty_nodes) {
  df.clusters.dim <- c(dim(df.cluster01)[1], dim(df.cluster02)[1], dim(df.cluster03)[1], dim(df.cluster04)[1], dim(df.cluster05)[1])
  barplot(df.clusters.dim,
          names.arg = c("cluster01", "cluster02", "cluster03", "cluster04", "cluster05"),
          col = "steelblue1")
}

DistribuciĂ³n de los datos

if (!empty_nodes) mpr.hist(df.cluster01)

if (!empty_nodes) mpr.hist(df.cluster02)

if (!empty_nodes) mpr.hist(df.cluster03)

if (!empty_nodes) mpr.hist(df.cluster04)

if (!empty_nodes) mpr.hist(df.cluster05)

if (!empty_nodes) mpr.boxplot(df.cluster01)
if (!empty_nodes) mpr.boxplot(df.cluster02)

if (!empty_nodes) mpr.boxplot(df.cluster03)

if (!empty_nodes) mpr.boxplot(df.cluster04)

if (!empty_nodes) mpr.boxplot(df.cluster05)

LocalizaciĂ³n geogrĂ¡fica de las estaciones de medida de cada cluster

# Agrupa por longitud y latitud para rellenar el mapa con menos datos.
if (!empty_nodes) {
  df.cluster01.grouped <- mpr.group_by_geo(df.cluster01)
  df.cluster02.grouped <- mpr.group_by_geo(df.cluster02)
  df.cluster03.grouped <- mpr.group_by_geo(df.cluster03)
  df.cluster04.grouped <- mpr.group_by_geo(df.cluster04)
  df.cluster05.grouped <- mpr.group_by_geo(df.cluster05)
}
if (!empty_nodes) mpr.draw_map(spain, df.cluster01.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster02.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster03.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster04.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster05.grouped)

VisualizaciĂ³n de 6 clĂºsteres:

if (!empty_nodes) {
  plot(hac, hang=-1, labels=F)
  rect.hclust(hac, k=6)
}

VisualizaciĂ³n de los clĂºsters en el mapa

A quĂ© clĂºster pertenece cada nodo del mapa de kohonen:

if (!empty_nodes) {
  groups <- cutree(hac, k=6)
  plot(model, type="mapping",
    bgcol=c("steelblue1","sienna1","yellowgreen","red","blue","yellow","purple","green","white","#1f77b4", '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2')[groups],
    shape = "straight", labels = "")
  add.cluster.boundaries(model, clustering=groups)
}

AnĂ¡lisis de las observaciones de cada cluster

if (!empty_nodes) {
  # Asignamos a cada registro su clĂºster
  df$cluster <- groups[model$unit.classif]
}

Nuevos dataframes por cluster

if (!empty_nodes) {
  # Creo nuevos dataframes, uno por cada clĂºster.
  df.cluster01 <- subset(df, cluster==1)
  df.cluster02 <- subset(df, cluster==2)
  df.cluster03 <- subset(df, cluster==3)
  df.cluster04 <- subset(df, cluster==4)
  df.cluster05 <- subset(df, cluster==5)
  df.cluster06 <- subset(df, cluster==6)

  # Extraigo del dataframe las features.
  df.cluster01 <- select(df.cluster01, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster02 <- select(df.cluster02, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster03 <- select(df.cluster03, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster04 <- select(df.cluster04, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster05 <- select(df.cluster05, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster06 <- select(df.cluster06, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
}
if (!empty_nodes) summary(df.cluster01)
   fecha_cnt          tmax            tmin             precip     
 Min.   :1.000   Min.   :-53.0   Min.   :-121.00   Min.   : 0.00  
 1st Qu.:2.000   1st Qu.:134.0   1st Qu.:  31.00   1st Qu.: 6.00  
 Median :3.000   Median :169.0   Median :  66.00   Median :12.00  
 Mean   :3.128   Mean   :167.9   Mean   :  66.89   Mean   :13.92  
 3rd Qu.:4.000   3rd Qu.:204.0   3rd Qu.: 101.00   3rd Qu.:21.00  
 Max.   :6.000   Max.   :342.0   Max.   : 213.00   Max.   :39.00  
     nevada           prof_nieve           longitud        latitud        
 Min.   :0.000000   Min.   :   0.0000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.000000   1st Qu.:   0.0000   1st Qu.:38.88   1st Qu.: -5.5975  
 Median :0.000000   Median :   0.0000   Median :40.87   Median : -2.9056  
 Mean   :0.000535   Mean   :   0.4659   Mean   :39.82   Mean   : -3.1711  
 3rd Qu.:0.000000   3rd Qu.:   0.0000   3rd Qu.:42.00   3rd Qu.:  0.4914  
 Max.   :6.000000   Max.   :1494.0000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  44.0  
 Median : 263.0  
 Mean   : 425.4  
 3rd Qu.: 674.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster02)
   fecha_cnt          tmax            tmin             precip          nevada 
 Min.   :1.000   Min.   :-50.0   Min.   :-115.00   Min.   :39.00   Min.   :0  
 1st Qu.:2.000   1st Qu.:118.0   1st Qu.:  42.00   1st Qu.:44.00   1st Qu.:0  
 Median :3.000   Median :146.0   Median :  68.00   Median :50.00   Median :0  
 Mean   :3.219   Mean   :143.4   Mean   :  63.73   Mean   :54.52   Mean   :0  
 3rd Qu.:5.000   3rd Qu.:176.0   3rd Qu.:  93.00   3rd Qu.:62.00   3rd Qu.:0  
 Max.   :7.000   Max.   :352.0   Max.   : 210.00   Max.   :95.00   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   :   0.000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.:   0.000   1st Qu.:40.82   1st Qu.: -6.0556   1st Qu.:  47.0  
 Median :   0.000   Median :42.43   Median : -3.7994   Median : 251.0  
 Mean   :   4.385   Mean   :41.40   Mean   : -3.4961   Mean   : 514.5  
 3rd Qu.:   0.000   3rd Qu.:43.31   3rd Qu.:  0.4914   3rd Qu.: 582.0  
 Max.   :1834.000   Max.   :43.57   Max.   :  4.2156   Max.   :2535.0  
if (!empty_nodes) summary(df.cluster03)
   fecha_cnt           tmax            tmin             precip      
 Min.   : 1.000   Min.   :-42.0   Min.   :-108.00   Min.   : 69.00  
 1st Qu.: 9.000   1st Qu.:123.0   1st Qu.:  58.00   1st Qu.: 78.00  
 Median :11.000   Median :152.0   Median :  85.00   Median : 92.00  
 Mean   : 9.227   Mean   :153.7   Mean   :  84.87   Mean   : 97.12  
 3rd Qu.:11.000   3rd Qu.:189.0   3rd Qu.: 115.00   3rd Qu.:110.00  
 Max.   :12.000   Max.   :336.0   Max.   : 219.00   Max.   :177.00  
     nevada    prof_nieve         longitud        latitud       
 Min.   :0   Min.   :  0.000   Min.   :27.82   Min.   :-17.889  
 1st Qu.:0   1st Qu.:  0.000   1st Qu.:40.78   1st Qu.: -8.411  
 Median :0   Median :  0.000   Median :42.43   Median : -4.488  
 Mean   :0   Mean   :  2.297   Mean   :41.23   Mean   : -4.677  
 3rd Qu.:0   3rd Qu.:  0.000   3rd Qu.:43.31   3rd Qu.: -1.787  
 Max.   :0   Max.   :892.000   Max.   :43.57   Max.   :  4.216  
    altitud      
 Min.   :   1.0  
 1st Qu.:  32.0  
 Median : 108.0  
 Mean   : 372.9  
 3rd Qu.: 370.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster04)
   fecha_cnt           tmax            tmin            precip          nevada 
 Min.   : 1.000   Min.   : 11.0   Min.   :-27.00   Min.   :173.0   Min.   :0  
 1st Qu.: 2.000   1st Qu.:115.0   1st Qu.: 53.00   1st Qu.:185.0   1st Qu.:0  
 Median : 6.000   Median :136.0   Median : 80.00   Median :205.0   Median :0  
 Mean   : 6.328   Mean   :151.5   Mean   : 84.85   Mean   :219.4   Mean   :0  
 3rd Qu.:11.000   3rd Qu.:158.0   3rd Qu.: 97.00   3rd Qu.:231.0   3rd Qu.:0  
 Max.   :12.000   Max.   :350.0   Max.   :223.00   Max.   :422.0   Max.   :0  
   prof_nieve     longitud        latitud           altitud      
 Min.   :  0   Min.   :28.31   Min.   :-16.499   Min.   :   4.0  
 1st Qu.:  0   1st Qu.:40.66   1st Qu.: -8.624   1st Qu.:  82.0  
 Median :  0   Median :42.24   Median : -8.411   Median : 261.0  
 Mean   : 10   Mean   :40.75   Mean   : -7.011   Mean   : 411.3  
 3rd Qu.:  0   3rd Qu.:42.89   3rd Qu.: -5.600   3rd Qu.: 370.0  
 Max.   :607   Max.   :43.36   Max.   :  2.825   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster05)
   fecha_cnt          tmax            tmin            precip          nevada 
 Min.   : 7.00   Min.   :-17.0   Min.   :-80.00   Min.   :23.00   Min.   :0  
 1st Qu.: 9.00   1st Qu.:140.0   1st Qu.: 64.00   1st Qu.:29.00   1st Qu.:0  
 Median :10.00   Median :184.0   Median :100.00   Median :36.00   Median :0  
 Mean   :10.19   Mean   :182.7   Mean   : 98.63   Mean   :38.61   Mean   :0  
 3rd Qu.:11.00   3rd Qu.:226.0   3rd Qu.:138.00   3rd Qu.:46.00   3rd Qu.:0  
 Max.   :12.00   Max.   :340.0   Max.   :244.00   Max.   :68.00   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   :  0.0000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.:  0.0000   1st Qu.:40.30   1st Qu.: -5.6614   1st Qu.:  43.3  
 Median :  0.0000   Median :41.96   Median : -2.9056   Median : 185.0  
 Mean   :  0.3201   Mean   :40.98   Mean   : -2.9424   Mean   : 440.5  
 3rd Qu.:  0.0000   3rd Qu.:42.89   3rd Qu.:  0.7789   3rd Qu.: 617.0  
 Max.   :338.0000   Max.   :43.57   Max.   :  4.2156   Max.   :2535.0  
if (!empty_nodes) summary(df.cluster06)
   fecha_cnt           tmax            tmin            precip      
 Min.   : 5.000   Min.   :-24.0   Min.   :-110.0   Min.   : 0.000  
 1st Qu.: 7.000   1st Qu.:192.0   1st Qu.:  88.0   1st Qu.: 1.000  
 Median : 9.000   Median :249.0   Median : 139.0   Median : 4.000  
 Mean   : 8.672   Mean   :236.9   Mean   : 128.4   Mean   : 6.587  
 3rd Qu.:10.000   3rd Qu.:289.0   3rd Qu.: 176.0   3rd Qu.:11.000  
 Max.   :12.000   Max.   :403.0   Max.   : 254.0   Max.   :25.000  
     nevada            prof_nieve          longitud        latitud        
 Min.   :0.0000000   Min.   : 0.00000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.0000000   1st Qu.: 0.00000   1st Qu.:37.79   1st Qu.: -5.6492  
 Median :0.0000000   Median : 0.00000   Median :40.41   Median : -3.4503  
 Mean   :0.0002026   Mean   : 0.03115   Mean   :39.04   Mean   : -3.6869  
 3rd Qu.:0.0000000   3rd Qu.: 0.00000   3rd Qu.:41.66   3rd Qu.:  0.3994  
 Max.   :3.0000000   Max.   :77.00000   Max.   :43.57   Max.   :  4.2156  
    altitud    
 Min.   :   1  
 1st Qu.:  35  
 Median : 192  
 Mean   : 401  
 3rd Qu.: 656  
 Max.   :2535  

NĂºmero de elementos en cada clĂºster

if (!empty_nodes) {
  df.clusters.dim <- c(dim(df.cluster01)[1], dim(df.cluster02)[1], dim(df.cluster03)[1], dim(df.cluster04)[1], dim(df.cluster05)[1], dim(df.cluster06)[1])
  barplot(df.clusters.dim,
          names.arg = c("cluster01", "cluster02", "cluster03", "cluster04", "cluster05", "cluster06"),
          col = "steelblue1")
}

DistribuciĂ³n de los datos

if (!empty_nodes) mpr.hist(df.cluster01)

if (!empty_nodes) mpr.hist(df.cluster02)

if (!empty_nodes) mpr.hist(df.cluster03)

if (!empty_nodes) mpr.hist(df.cluster04)

if (!empty_nodes) mpr.hist(df.cluster05)

if (!empty_nodes) mpr.hist(df.cluster06)

if (!empty_nodes) mpr.boxplot(df.cluster01)
if (!empty_nodes) mpr.boxplot(df.cluster02)

if (!empty_nodes) mpr.boxplot(df.cluster03)

if (!empty_nodes) mpr.boxplot(df.cluster04)

if (!empty_nodes) mpr.boxplot(df.cluster05)

if (!empty_nodes) mpr.boxplot(df.cluster06)

LocalizaciĂ³n geogrĂ¡fica de las estaciones de medida de cada cluster

# Agrupa por longitud y latitud para rellenar el mapa con menos datos.
if (!empty_nodes) {
  df.cluster01.grouped <- mpr.group_by_geo(df.cluster01)
  df.cluster02.grouped <- mpr.group_by_geo(df.cluster02)
  df.cluster03.grouped <- mpr.group_by_geo(df.cluster03)
  df.cluster04.grouped <- mpr.group_by_geo(df.cluster04)
  df.cluster05.grouped <- mpr.group_by_geo(df.cluster05)
  df.cluster06.grouped <- mpr.group_by_geo(df.cluster06)
}
if (!empty_nodes) mpr.draw_map(spain, df.cluster01.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster02.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster03.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster04.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster05.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster06.grouped)

VisualizaciĂ³n de 8 clĂºsteres:

if (!empty_nodes) {
  plot(hac, hang=-1, labels=F)
  rect.hclust(hac, k=8)
}

VisualizaciĂ³n de los clĂºsters en el mapa

A quĂ© clĂºster pertenece cada nodo del mapa de kohonen:

if (!empty_nodes) {
  groups <- cutree(hac, k=8)
  plot(model, type="mapping",
    bgcol=c("steelblue1","sienna1","yellowgreen","red","blue","yellow","purple","green","white","#1f77b4", '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2')[groups],
    shape = "straight", labels = "")
  add.cluster.boundaries(model, clustering=groups)
}

AnĂ¡lisis de las observaciones de cada cluster

if (!empty_nodes) {
  # Asignamos a cada registro su clĂºster
  df$cluster <- groups[model$unit.classif]
}

Nuevos dataframes por cluster

if (!empty_nodes) {
  # Creo nuevos dataframes, uno por cada clĂºster.
  df.cluster01 <- subset(df, cluster==1)
  df.cluster02 <- subset(df, cluster==2)
  df.cluster03 <- subset(df, cluster==3)
  df.cluster04 <- subset(df, cluster==4)
  df.cluster05 <- subset(df, cluster==5)
  df.cluster06 <- subset(df, cluster==6)
  df.cluster07 <- subset(df, cluster==7)
  df.cluster08 <- subset(df, cluster==8)

  # Extraigo del dataframe las features.
  df.cluster01 <- select(df.cluster01, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster02 <- select(df.cluster02, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster03 <- select(df.cluster03, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster04 <- select(df.cluster04, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster05 <- select(df.cluster05, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster06 <- select(df.cluster06, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster07 <- select(df.cluster07, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster08 <- select(df.cluster08, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
}
if (!empty_nodes) summary(df.cluster01)
   fecha_cnt          tmax            tmin             precip     
 Min.   :1.000   Min.   :-53.0   Min.   :-121.00   Min.   : 0.00  
 1st Qu.:2.000   1st Qu.:134.0   1st Qu.:  31.00   1st Qu.: 6.00  
 Median :3.000   Median :169.0   Median :  66.00   Median :12.00  
 Mean   :3.128   Mean   :167.9   Mean   :  66.89   Mean   :13.92  
 3rd Qu.:4.000   3rd Qu.:204.0   3rd Qu.: 101.00   3rd Qu.:21.00  
 Max.   :6.000   Max.   :342.0   Max.   : 213.00   Max.   :39.00  
     nevada           prof_nieve           longitud        latitud        
 Min.   :0.000000   Min.   :   0.0000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.000000   1st Qu.:   0.0000   1st Qu.:38.88   1st Qu.: -5.5975  
 Median :0.000000   Median :   0.0000   Median :40.87   Median : -2.9056  
 Mean   :0.000535   Mean   :   0.4659   Mean   :39.82   Mean   : -3.1711  
 3rd Qu.:0.000000   3rd Qu.:   0.0000   3rd Qu.:42.00   3rd Qu.:  0.4914  
 Max.   :6.000000   Max.   :1494.0000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  44.0  
 Median : 263.0  
 Mean   : 425.4  
 3rd Qu.: 674.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster02)
   fecha_cnt          tmax            tmin             precip          nevada 
 Min.   :1.000   Min.   :-50.0   Min.   :-115.00   Min.   :39.00   Min.   :0  
 1st Qu.:2.000   1st Qu.:118.0   1st Qu.:  42.00   1st Qu.:44.00   1st Qu.:0  
 Median :3.000   Median :146.0   Median :  68.00   Median :50.00   Median :0  
 Mean   :3.219   Mean   :143.4   Mean   :  63.73   Mean   :54.52   Mean   :0  
 3rd Qu.:5.000   3rd Qu.:176.0   3rd Qu.:  93.00   3rd Qu.:62.00   3rd Qu.:0  
 Max.   :7.000   Max.   :352.0   Max.   : 210.00   Max.   :95.00   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   :   0.000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.:   0.000   1st Qu.:40.82   1st Qu.: -6.0556   1st Qu.:  47.0  
 Median :   0.000   Median :42.43   Median : -3.7994   Median : 251.0  
 Mean   :   4.385   Mean   :41.40   Mean   : -3.4961   Mean   : 514.5  
 3rd Qu.:   0.000   3rd Qu.:43.31   3rd Qu.:  0.4914   3rd Qu.: 582.0  
 Max.   :1834.000   Max.   :43.57   Max.   :  4.2156   Max.   :2535.0  
if (!empty_nodes) summary(df.cluster03)
   fecha_cnt          tmax            tmin             precip          nevada 
 Min.   :1.000   Min.   :-42.0   Min.   :-108.00   Min.   : 95.0   Min.   :0  
 1st Qu.:1.000   1st Qu.:109.0   1st Qu.:  39.00   1st Qu.:102.0   1st Qu.:0  
 Median :2.000   Median :127.0   Median :  61.00   Median :110.0   Median :0  
 Mean   :2.305   Mean   :127.1   Mean   :  59.51   Mean   :115.1   Mean   :0  
 3rd Qu.:3.000   3rd Qu.:150.8   3rd Qu.:  83.00   3rd Qu.:126.0   3rd Qu.:0  
 Max.   :6.000   Max.   :280.0   Max.   : 162.00   Max.   :169.0   Max.   :0  
   prof_nieve         longitud        latitud           altitud      
 Min.   :  0.000   Min.   :27.82   Min.   :-17.889   Min.   :   1.0  
 1st Qu.:  0.000   1st Qu.:41.98   1st Qu.: -8.624   1st Qu.:  42.0  
 Median :  0.000   Median :42.43   Median : -8.411   Median : 261.0  
 Mean   :  6.262   Mean   :41.43   Mean   : -5.887   Mean   : 399.5  
 3rd Qu.:  0.000   3rd Qu.:42.89   3rd Qu.: -2.039   3rd Qu.: 370.0  
 Max.   :892.000   Max.   :43.57   Max.   :  2.827   Max.   :2519.0  
if (!empty_nodes) summary(df.cluster04)
   fecha_cnt           tmax            tmin            precip          nevada 
 Min.   : 1.000   Min.   : 11.0   Min.   :-27.00   Min.   :173.0   Min.   :0  
 1st Qu.: 2.000   1st Qu.:115.0   1st Qu.: 53.00   1st Qu.:185.0   1st Qu.:0  
 Median : 6.000   Median :136.0   Median : 80.00   Median :205.0   Median :0  
 Mean   : 6.328   Mean   :151.5   Mean   : 84.85   Mean   :219.4   Mean   :0  
 3rd Qu.:11.000   3rd Qu.:158.0   3rd Qu.: 97.00   3rd Qu.:231.0   3rd Qu.:0  
 Max.   :12.000   Max.   :350.0   Max.   :223.00   Max.   :422.0   Max.   :0  
   prof_nieve     longitud        latitud           altitud      
 Min.   :  0   Min.   :28.31   Min.   :-16.499   Min.   :   4.0  
 1st Qu.:  0   1st Qu.:40.66   1st Qu.: -8.624   1st Qu.:  82.0  
 Median :  0   Median :42.24   Median : -8.411   Median : 261.0  
 Mean   : 10   Mean   :40.75   Mean   : -7.011   Mean   : 411.3  
 3rd Qu.:  0   3rd Qu.:42.89   3rd Qu.: -5.600   3rd Qu.: 370.0  
 Max.   :607   Max.   :43.36   Max.   :  2.825   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster05)
   fecha_cnt          tmax            tmin            precip        nevada 
 Min.   : 6.00   Min.   : -8.0   Min.   :-58.00   Min.   :106   Min.   :0  
 1st Qu.:10.00   1st Qu.:125.5   1st Qu.: 64.00   1st Qu.:113   1st Qu.:0  
 Median :11.00   Median :149.0   Median : 86.00   Median :122   Median :0  
 Mean   :10.85   Mean   :151.1   Mean   : 85.38   Mean   :127   Mean   :0  
 3rd Qu.:12.00   3rd Qu.:181.5   3rd Qu.:111.00   3rd Qu.:138   3rd Qu.:0  
 Max.   :12.00   Max.   :326.0   Max.   :207.00   Max.   :177   Max.   :0  
   prof_nieve         longitud        latitud           altitud       
 Min.   : 0.0000   Min.   :28.31   Min.   :-17.755   Min.   :   1.00  
 1st Qu.: 0.0000   1st Qu.:40.80   1st Qu.: -8.518   1st Qu.:  28.05  
 Median : 0.0000   Median :42.44   Median : -5.600   Median : 200.00  
 Mean   : 0.9257   Mean   :41.23   Mean   : -5.237   Mean   : 394.77  
 3rd Qu.: 0.0000   3rd Qu.:43.30   3rd Qu.: -1.913   3rd Qu.: 370.00  
 Max.   :81.0000   Max.   :43.57   Max.   :  3.035   Max.   :2535.00  
if (!empty_nodes) summary(df.cluster06)
   fecha_cnt          tmax            tmin            precip          nevada 
 Min.   : 7.00   Min.   :-17.0   Min.   :-80.00   Min.   :23.00   Min.   :0  
 1st Qu.: 9.00   1st Qu.:140.0   1st Qu.: 64.00   1st Qu.:29.00   1st Qu.:0  
 Median :10.00   Median :184.0   Median :100.00   Median :36.00   Median :0  
 Mean   :10.19   Mean   :182.7   Mean   : 98.63   Mean   :38.61   Mean   :0  
 3rd Qu.:11.00   3rd Qu.:226.0   3rd Qu.:138.00   3rd Qu.:46.00   3rd Qu.:0  
 Max.   :12.00   Max.   :340.0   Max.   :244.00   Max.   :68.00   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   :  0.0000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.:  0.0000   1st Qu.:40.30   1st Qu.: -5.6614   1st Qu.:  43.3  
 Median :  0.0000   Median :41.96   Median : -2.9056   Median : 185.0  
 Mean   :  0.3201   Mean   :40.98   Mean   : -2.9424   Mean   : 440.5  
 3rd Qu.:  0.0000   3rd Qu.:42.89   3rd Qu.:  0.7789   3rd Qu.: 617.0  
 Max.   :338.0000   Max.   :43.57   Max.   :  4.2156   Max.   :2535.0  
if (!empty_nodes) summary(df.cluster07)
   fecha_cnt          tmax            tmin            precip           nevada 
 Min.   : 7.00   Min.   : -7.0   Min.   :-69.00   Min.   : 69.00   Min.   :0  
 1st Qu.:10.00   1st Qu.:131.0   1st Qu.: 66.00   1st Qu.: 74.00   1st Qu.:0  
 Median :11.00   Median :164.0   Median : 93.00   Median : 81.00   Median :0  
 Mean   :10.71   Mean   :162.1   Mean   : 91.97   Mean   : 82.69   Mean   :0  
 3rd Qu.:12.00   3rd Qu.:200.0   3rd Qu.:125.00   3rd Qu.: 90.00   3rd Qu.:0  
 Max.   :12.00   Max.   :336.0   Max.   :219.00   Max.   :105.00   Max.   :0  
   prof_nieve         longitud        latitud           altitud      
 Min.   :  0.000   Min.   :27.82   Min.   :-17.889   Min.   :   1.0  
 1st Qu.:  0.000   1st Qu.:40.78   1st Qu.: -7.076   1st Qu.:  32.0  
 Median :  0.000   Median :42.43   Median : -3.831   Median :  98.0  
 Mean   :  1.587   Mean   :41.17   Mean   : -4.156   Mean   : 358.5  
 3rd Qu.:  0.000   3rd Qu.:43.31   3rd Qu.: -1.787   3rd Qu.: 361.8  
 Max.   :382.000   Max.   :43.57   Max.   :  4.216   Max.   :2535.0  
if (!empty_nodes) summary(df.cluster08)
   fecha_cnt           tmax            tmin            precip      
 Min.   : 5.000   Min.   :-24.0   Min.   :-110.0   Min.   : 0.000  
 1st Qu.: 7.000   1st Qu.:192.0   1st Qu.:  88.0   1st Qu.: 1.000  
 Median : 9.000   Median :249.0   Median : 139.0   Median : 4.000  
 Mean   : 8.672   Mean   :236.9   Mean   : 128.4   Mean   : 6.587  
 3rd Qu.:10.000   3rd Qu.:289.0   3rd Qu.: 176.0   3rd Qu.:11.000  
 Max.   :12.000   Max.   :403.0   Max.   : 254.0   Max.   :25.000  
     nevada            prof_nieve          longitud        latitud        
 Min.   :0.0000000   Min.   : 0.00000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.0000000   1st Qu.: 0.00000   1st Qu.:37.79   1st Qu.: -5.6492  
 Median :0.0000000   Median : 0.00000   Median :40.41   Median : -3.4503  
 Mean   :0.0002026   Mean   : 0.03115   Mean   :39.04   Mean   : -3.6869  
 3rd Qu.:0.0000000   3rd Qu.: 0.00000   3rd Qu.:41.66   3rd Qu.:  0.3994  
 Max.   :3.0000000   Max.   :77.00000   Max.   :43.57   Max.   :  4.2156  
    altitud    
 Min.   :   1  
 1st Qu.:  35  
 Median : 192  
 Mean   : 401  
 3rd Qu.: 656  
 Max.   :2535  

NĂºmero de elementos en cada clĂºster

if (!empty_nodes) {
  df.clusters.dim <- c(dim(df.cluster01)[1], dim(df.cluster02)[1], dim(df.cluster03)[1], dim(df.cluster04)[1], dim(df.cluster05)[1], dim(df.cluster06)[1], dim(df.cluster07)[1], dim(df.cluster08)[1])
  barplot(df.clusters.dim,
          names.arg = c("cluster01", "cluster02", "cluster03", "cluster04", "cluster05", "cluster06", "cluster07", "cluster08"),
          col = "steelblue1")
}

DistribuciĂ³n de los datos

if (!empty_nodes) mpr.hist(df.cluster01)

if (!empty_nodes) mpr.hist(df.cluster02)

if (!empty_nodes) mpr.hist(df.cluster03)

if (!empty_nodes) mpr.hist(df.cluster04)

if (!empty_nodes) mpr.hist(df.cluster05)

if (!empty_nodes) mpr.hist(df.cluster06)

if (!empty_nodes) mpr.hist(df.cluster07)

if (!empty_nodes) mpr.hist(df.cluster08)

if (!empty_nodes) mpr.boxplot(df.cluster01)
if (!empty_nodes) mpr.boxplot(df.cluster02)

if (!empty_nodes) mpr.boxplot(df.cluster03)

if (!empty_nodes) mpr.boxplot(df.cluster04)

if (!empty_nodes) mpr.boxplot(df.cluster05)

if (!empty_nodes) mpr.boxplot(df.cluster06)

if (!empty_nodes) mpr.boxplot(df.cluster07)

if (!empty_nodes) mpr.boxplot(df.cluster08)

LocalizaciĂ³n geogrĂ¡fica de las estaciones de medida de cada cluster

# Agrupa por longitud y latitud para rellenar el mapa con menos datos.
if (!empty_nodes) {
  df.cluster01.grouped <- mpr.group_by_geo(df.cluster01)
  df.cluster02.grouped <- mpr.group_by_geo(df.cluster02)
  df.cluster03.grouped <- mpr.group_by_geo(df.cluster03)
  df.cluster04.grouped <- mpr.group_by_geo(df.cluster04)
  df.cluster05.grouped <- mpr.group_by_geo(df.cluster05)
  df.cluster06.grouped <- mpr.group_by_geo(df.cluster06)
  df.cluster07.grouped <- mpr.group_by_geo(df.cluster07)
  df.cluster08.grouped <- mpr.group_by_geo(df.cluster08)
}
if (!empty_nodes) mpr.draw_map(spain, df.cluster01.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster02.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster03.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster04.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster05.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster06.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster07.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster08.grouped)

VisualizaciĂ³n de 10 clĂºsteres:

if (!empty_nodes) {
  plot(hac, hang=-1, labels=F)
  rect.hclust(hac, k=10)
}

VisualizaciĂ³n de los clĂºsters en el mapa

A quĂ© clĂºster pertenece cada nodo del mapa de kohonen:

if (!empty_nodes) {
  groups <- cutree(hac, k=10)
  plot(model, type="mapping",
    bgcol=c("steelblue1","sienna1","yellowgreen","red","blue","yellow","purple","green","white","#1f77b4", '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2')[groups],
    shape = "straight", labels = "")
  add.cluster.boundaries(model, clustering=groups)
}

AnĂ¡lisis de las observaciones de cada cluster

if (!empty_nodes) {
  # Asignamos a cada registro su clĂºster
  df$cluster <- groups[model$unit.classif]
}

Nuevos dataframes por cluster

if (!empty_nodes) {
  # Creo nuevos dataframes, uno por cada clĂºster.
  df.cluster01 <- subset(df, cluster==1)
  df.cluster02 <- subset(df, cluster==2)
  df.cluster03 <- subset(df, cluster==3)
  df.cluster04 <- subset(df, cluster==4)
  df.cluster05 <- subset(df, cluster==5)
  df.cluster06 <- subset(df, cluster==6)
  df.cluster07 <- subset(df, cluster==7)
  df.cluster08 <- subset(df, cluster==8)
  df.cluster09 <- subset(df, cluster==9)
  df.cluster10 <- subset(df, cluster==10)

  # Extraigo del dataframe las features.
  df.cluster01 <- select(df.cluster01, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster02 <- select(df.cluster02, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster03 <- select(df.cluster03, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster04 <- select(df.cluster04, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster05 <- select(df.cluster05, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster06 <- select(df.cluster06, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster07 <- select(df.cluster07, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster08 <- select(df.cluster08, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster09 <- select(df.cluster09, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
  df.cluster10 <- select(df.cluster10, fecha_cnt, tmax, tmin, precip, nevada, prof_nieve, longitud, latitud, altitud)
}
if (!empty_nodes) summary(df.cluster01)
   fecha_cnt          tmax            tmin             precip      
 Min.   :1.000   Min.   :-46.0   Min.   :-121.00   Min.   : 0.000  
 1st Qu.:1.000   1st Qu.:119.0   1st Qu.:  17.00   1st Qu.: 2.000  
 Median :2.000   Median :156.0   Median :  48.00   Median : 6.000  
 Mean   :2.217   Mean   :152.2   Mean   :  52.29   Mean   : 6.864  
 3rd Qu.:3.000   3rd Qu.:187.0   3rd Qu.:  83.00   3rd Qu.:10.000  
 Max.   :4.000   Max.   :300.0   Max.   : 193.00   Max.   :23.000  
     nevada           prof_nieve          longitud        latitud        
 Min.   :0.000000   Min.   :  0.0000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.000000   1st Qu.:  0.0000   1st Qu.:37.96   1st Qu.: -5.5975  
 Median :0.000000   Median :  0.0000   Median :40.41   Median : -2.4831  
 Mean   :0.001044   Mean   :  0.3035   Mean   :39.01   Mean   : -3.4370  
 3rd Qu.:0.000000   3rd Qu.:  0.0000   3rd Qu.:41.63   3rd Qu.:  0.4914  
 Max.   :6.000000   Max.   :626.0000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  35.0  
 Median : 200.0  
 Mean   : 398.4  
 3rd Qu.: 667.0  
 Max.   :2535.0  
if (!empty_nodes) summary(df.cluster02)
   fecha_cnt          tmax            tmin             precip          nevada 
 Min.   :1.000   Min.   :-50.0   Min.   :-115.00   Min.   :39.00   Min.   :0  
 1st Qu.:2.000   1st Qu.:118.0   1st Qu.:  42.00   1st Qu.:44.00   1st Qu.:0  
 Median :3.000   Median :146.0   Median :  68.00   Median :50.00   Median :0  
 Mean   :3.219   Mean   :143.4   Mean   :  63.73   Mean   :54.52   Mean   :0  
 3rd Qu.:5.000   3rd Qu.:176.0   3rd Qu.:  93.00   3rd Qu.:62.00   3rd Qu.:0  
 Max.   :7.000   Max.   :352.0   Max.   : 210.00   Max.   :95.00   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   :   0.000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.:   0.000   1st Qu.:40.82   1st Qu.: -6.0556   1st Qu.:  47.0  
 Median :   0.000   Median :42.43   Median : -3.7994   Median : 251.0  
 Mean   :   4.385   Mean   :41.40   Mean   : -3.4961   Mean   : 514.5  
 3rd Qu.:   0.000   3rd Qu.:43.31   3rd Qu.:  0.4914   3rd Qu.: 582.0  
 Max.   :1834.000   Max.   :43.57   Max.   :  4.2156   Max.   :2535.0  
if (!empty_nodes) summary(df.cluster03)
   fecha_cnt          tmax            tmin             precip          nevada 
 Min.   :1.000   Min.   :-42.0   Min.   :-108.00   Min.   : 95.0   Min.   :0  
 1st Qu.:1.000   1st Qu.:109.0   1st Qu.:  39.00   1st Qu.:102.0   1st Qu.:0  
 Median :2.000   Median :127.0   Median :  61.00   Median :110.0   Median :0  
 Mean   :2.305   Mean   :127.1   Mean   :  59.51   Mean   :115.1   Mean   :0  
 3rd Qu.:3.000   3rd Qu.:150.8   3rd Qu.:  83.00   3rd Qu.:126.0   3rd Qu.:0  
 Max.   :6.000   Max.   :280.0   Max.   : 162.00   Max.   :169.0   Max.   :0  
   prof_nieve         longitud        latitud           altitud      
 Min.   :  0.000   Min.   :27.82   Min.   :-17.889   Min.   :   1.0  
 1st Qu.:  0.000   1st Qu.:41.98   1st Qu.: -8.624   1st Qu.:  42.0  
 Median :  0.000   Median :42.43   Median : -8.411   Median : 261.0  
 Mean   :  6.262   Mean   :41.43   Mean   : -5.887   Mean   : 399.5  
 3rd Qu.:  0.000   3rd Qu.:42.89   3rd Qu.: -2.039   3rd Qu.: 370.0  
 Max.   :892.000   Max.   :43.57   Max.   :  2.827   Max.   :2519.0  
if (!empty_nodes) summary(df.cluster04)
   fecha_cnt           tmax            tmin            precip          nevada 
 Min.   : 1.000   Min.   : 11.0   Min.   :-27.00   Min.   :173.0   Min.   :0  
 1st Qu.: 2.000   1st Qu.:115.0   1st Qu.: 53.00   1st Qu.:185.0   1st Qu.:0  
 Median : 6.000   Median :136.0   Median : 80.00   Median :205.0   Median :0  
 Mean   : 6.328   Mean   :151.5   Mean   : 84.85   Mean   :219.4   Mean   :0  
 3rd Qu.:11.000   3rd Qu.:158.0   3rd Qu.: 97.00   3rd Qu.:231.0   3rd Qu.:0  
 Max.   :12.000   Max.   :350.0   Max.   :223.00   Max.   :422.0   Max.   :0  
   prof_nieve     longitud        latitud           altitud      
 Min.   :  0   Min.   :28.31   Min.   :-16.499   Min.   :   4.0  
 1st Qu.:  0   1st Qu.:40.66   1st Qu.: -8.624   1st Qu.:  82.0  
 Median :  0   Median :42.24   Median : -8.411   Median : 261.0  
 Mean   : 10   Mean   :40.75   Mean   : -7.011   Mean   : 411.3  
 3rd Qu.:  0   3rd Qu.:42.89   3rd Qu.: -5.600   3rd Qu.: 370.0  
 Max.   :607   Max.   :43.36   Max.   :  2.825   Max.   :2371.0  
if (!empty_nodes) summary(df.cluster05)
   fecha_cnt          tmax            tmin             precip          nevada 
 Min.   :1.000   Min.   :-53.0   Min.   :-113.00   Min.   : 9.00   Min.   :0  
 1st Qu.:3.000   1st Qu.:151.0   1st Qu.:  52.00   1st Qu.:14.00   1st Qu.:0  
 Median :4.000   Median :184.0   Median :  83.00   Median :20.00   Median :0  
 Mean   :4.088   Mean   :184.4   Mean   :  82.27   Mean   :21.35   Mean   :0  
 3rd Qu.:5.000   3rd Qu.:220.0   3rd Qu.: 114.00   3rd Qu.:28.00   3rd Qu.:0  
 Max.   :6.000   Max.   :342.0   Max.   : 213.00   Max.   :39.00   Max.   :0  
   prof_nieve           longitud        latitud            altitud      
 Min.   :   0.0000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.:   0.0000   1st Qu.:39.86   1st Qu.: -5.5975   1st Qu.:  58.0  
 Median :   0.0000   Median :41.42   Median : -3.1642   Median : 353.0  
 Mean   :   0.6368   Mean   :40.68   Mean   : -2.8912   Mean   : 453.7  
 3rd Qu.:   0.0000   3rd Qu.:42.45   3rd Qu.:  0.4914   3rd Qu.: 687.0  
 Max.   :1494.0000   Max.   :43.57   Max.   :  4.2156   Max.   :2535.0  
if (!empty_nodes) summary(df.cluster06)
   fecha_cnt          tmax            tmin            precip        nevada 
 Min.   : 6.00   Min.   : -8.0   Min.   :-58.00   Min.   :106   Min.   :0  
 1st Qu.:10.00   1st Qu.:125.5   1st Qu.: 64.00   1st Qu.:113   1st Qu.:0  
 Median :11.00   Median :149.0   Median : 86.00   Median :122   Median :0  
 Mean   :10.85   Mean   :151.1   Mean   : 85.38   Mean   :127   Mean   :0  
 3rd Qu.:12.00   3rd Qu.:181.5   3rd Qu.:111.00   3rd Qu.:138   3rd Qu.:0  
 Max.   :12.00   Max.   :326.0   Max.   :207.00   Max.   :177   Max.   :0  
   prof_nieve         longitud        latitud           altitud       
 Min.   : 0.0000   Min.   :28.31   Min.   :-17.755   Min.   :   1.00  
 1st Qu.: 0.0000   1st Qu.:40.80   1st Qu.: -8.518   1st Qu.:  28.05  
 Median : 0.0000   Median :42.44   Median : -5.600   Median : 200.00  
 Mean   : 0.9257   Mean   :41.23   Mean   : -5.237   Mean   : 394.77  
 3rd Qu.: 0.0000   3rd Qu.:43.30   3rd Qu.: -1.913   3rd Qu.: 370.00  
 Max.   :81.0000   Max.   :43.57   Max.   :  3.035   Max.   :2535.00  
if (!empty_nodes) summary(df.cluster07)
   fecha_cnt          tmax            tmin            precip          nevada 
 Min.   : 7.00   Min.   :-17.0   Min.   :-80.00   Min.   :23.00   Min.   :0  
 1st Qu.: 9.00   1st Qu.:140.0   1st Qu.: 64.00   1st Qu.:29.00   1st Qu.:0  
 Median :10.00   Median :184.0   Median :100.00   Median :36.00   Median :0  
 Mean   :10.19   Mean   :182.7   Mean   : 98.63   Mean   :38.61   Mean   :0  
 3rd Qu.:11.00   3rd Qu.:226.0   3rd Qu.:138.00   3rd Qu.:46.00   3rd Qu.:0  
 Max.   :12.00   Max.   :340.0   Max.   :244.00   Max.   :68.00   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   :  0.0000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.:  0.0000   1st Qu.:40.30   1st Qu.: -5.6614   1st Qu.:  43.3  
 Median :  0.0000   Median :41.96   Median : -2.9056   Median : 185.0  
 Mean   :  0.3201   Mean   :40.98   Mean   : -2.9424   Mean   : 440.5  
 3rd Qu.:  0.0000   3rd Qu.:42.89   3rd Qu.:  0.7789   3rd Qu.: 617.0  
 Max.   :338.0000   Max.   :43.57   Max.   :  4.2156   Max.   :2535.0  
if (!empty_nodes) summary(df.cluster08)
   fecha_cnt          tmax            tmin            precip           nevada 
 Min.   : 7.00   Min.   : -7.0   Min.   :-69.00   Min.   : 69.00   Min.   :0  
 1st Qu.:10.00   1st Qu.:131.0   1st Qu.: 66.00   1st Qu.: 74.00   1st Qu.:0  
 Median :11.00   Median :164.0   Median : 93.00   Median : 81.00   Median :0  
 Mean   :10.71   Mean   :162.1   Mean   : 91.97   Mean   : 82.69   Mean   :0  
 3rd Qu.:12.00   3rd Qu.:200.0   3rd Qu.:125.00   3rd Qu.: 90.00   3rd Qu.:0  
 Max.   :12.00   Max.   :336.0   Max.   :219.00   Max.   :105.00   Max.   :0  
   prof_nieve         longitud        latitud           altitud      
 Min.   :  0.000   Min.   :27.82   Min.   :-17.889   Min.   :   1.0  
 1st Qu.:  0.000   1st Qu.:40.78   1st Qu.: -7.076   1st Qu.:  32.0  
 Median :  0.000   Median :42.43   Median : -3.831   Median :  98.0  
 Mean   :  1.587   Mean   :41.17   Mean   : -4.156   Mean   : 358.5  
 3rd Qu.:  0.000   3rd Qu.:43.31   3rd Qu.: -1.787   3rd Qu.: 361.8  
 Max.   :382.000   Max.   :43.57   Max.   :  4.216   Max.   :2535.0  
if (!empty_nodes) summary(df.cluster09)
   fecha_cnt          tmax            tmin           precip           nevada 
 Min.   :5.000   Min.   : 58.0   Min.   :-26.0   Min.   : 0.000   Min.   :0  
 1st Qu.:6.000   1st Qu.:250.0   1st Qu.:137.0   1st Qu.: 0.000   1st Qu.:0  
 Median :7.000   Median :283.0   Median :165.0   Median : 2.000   Median :0  
 Mean   :6.847   Mean   :279.5   Mean   :161.9   Mean   : 4.066   Mean   :0  
 3rd Qu.:8.000   3rd Qu.:310.0   3rd Qu.:190.0   3rd Qu.: 6.000   3rd Qu.:0  
 Max.   :8.000   Max.   :403.0   Max.   :254.0   Max.   :24.000   Max.   :0  
   prof_nieve          longitud        latitud            altitud      
 Min.   : 0.00000   Min.   :27.82   Min.   :-17.8889   Min.   :   1.0  
 1st Qu.: 0.00000   1st Qu.:37.42   1st Qu.: -5.7333   1st Qu.:  33.0  
 Median : 0.00000   Median :40.30   Median : -3.6781   Median : 148.0  
 Mean   : 0.00273   Mean   :38.89   Mean   : -3.9267   Mean   : 380.3  
 3rd Qu.: 0.00000   3rd Qu.:41.65   3rd Qu.:  0.3664   3rd Qu.: 628.0  
 Max.   :39.00000   Max.   :43.57   Max.   :  4.2156   Max.   :2535.0  
if (!empty_nodes) summary(df.cluster10)
   fecha_cnt          tmax            tmin             precip      
 Min.   : 9.00   Min.   :-24.0   Min.   :-110.00   Min.   : 0.000  
 1st Qu.: 9.00   1st Qu.:146.0   1st Qu.:  48.00   1st Qu.: 3.000  
 Median :10.00   Median :199.0   Median :  96.00   Median : 8.000  
 Mean   :10.46   Mean   :195.2   Mean   :  95.69   Mean   : 9.055  
 3rd Qu.:12.00   3rd Qu.:249.0   3rd Qu.: 144.00   3rd Qu.:15.000  
 Max.   :12.00   Max.   :401.0   Max.   : 236.00   Max.   :25.000  
     nevada            prof_nieve          longitud        latitud        
 Min.   :0.0000000   Min.   : 0.00000   Min.   :27.82   Min.   :-17.8889  
 1st Qu.:0.0000000   1st Qu.: 0.00000   1st Qu.:37.96   1st Qu.: -5.5975  
 Median :0.0000000   Median : 0.00000   Median :40.48   Median : -2.9553  
 Mean   :0.0004009   Mean   : 0.05897   Mean   :39.20   Mean   : -3.4523  
 3rd Qu.:0.0000000   3rd Qu.: 0.00000   3rd Qu.:41.66   3rd Qu.:  0.4914  
 Max.   :3.0000000   Max.   :77.00000   Max.   :43.57   Max.   :  4.2156  
    altitud      
 Min.   :   1.0  
 1st Qu.:  42.0  
 Median : 261.0  
 Mean   : 421.3  
 3rd Qu.: 674.0  
 Max.   :2535.0  

NĂºmero de elementos en cada clĂºster

if (!empty_nodes) {
  df.clusters.dim <- c(dim(df.cluster01)[1], dim(df.cluster02)[1], dim(df.cluster03)[1], dim(df.cluster04)[1], dim(df.cluster05)[1], dim(df.cluster06)[1], dim(df.cluster07)[1], dim(df.cluster08)[1], dim(df.cluster09)[1], dim(df.cluster10)[1])
  barplot(df.clusters.dim,
          names.arg = c("cluster01", "cluster02", "cluster03", "cluster04", "cluster05", "cluster06", "cluster07", "cluster08", "cluster09", "cluster10"),
          col = "steelblue1")
}

DistribuciĂ³n de los datos

if (!empty_nodes) mpr.hist(df.cluster01)

if (!empty_nodes) mpr.hist(df.cluster02)

if (!empty_nodes) mpr.hist(df.cluster03)

if (!empty_nodes) mpr.hist(df.cluster04)

if (!empty_nodes) mpr.hist(df.cluster05)

if (!empty_nodes) mpr.hist(df.cluster06)

if (!empty_nodes) mpr.hist(df.cluster07)

if (!empty_nodes) mpr.hist(df.cluster08)

if (!empty_nodes) mpr.hist(df.cluster09)

if (!empty_nodes) mpr.hist(df.cluster10)

if (!empty_nodes) mpr.boxplot(df.cluster01)
if (!empty_nodes) mpr.boxplot(df.cluster02)

if (!empty_nodes) mpr.boxplot(df.cluster03)

if (!empty_nodes) mpr.boxplot(df.cluster04)

if (!empty_nodes) mpr.boxplot(df.cluster05)

if (!empty_nodes) mpr.boxplot(df.cluster06)

if (!empty_nodes) mpr.boxplot(df.cluster07)

if (!empty_nodes) mpr.boxplot(df.cluster08)

if (!empty_nodes) mpr.boxplot(df.cluster09)

if (!empty_nodes) mpr.boxplot(df.cluster10)

LocalizaciĂ³n geogrĂ¡fica de las estaciones de medida de cada cluster

# Agrupa por longitud y latitud para rellenar el mapa con menos datos.
if (!empty_nodes) {
  df.cluster01.grouped <- mpr.group_by_geo(df.cluster01)
  df.cluster02.grouped <- mpr.group_by_geo(df.cluster02)
  df.cluster03.grouped <- mpr.group_by_geo(df.cluster03)
  df.cluster04.grouped <- mpr.group_by_geo(df.cluster04)
  df.cluster05.grouped <- mpr.group_by_geo(df.cluster05)
  df.cluster06.grouped <- mpr.group_by_geo(df.cluster06)
  df.cluster07.grouped <- mpr.group_by_geo(df.cluster07)
  df.cluster08.grouped <- mpr.group_by_geo(df.cluster08)
  df.cluster09.grouped <- mpr.group_by_geo(df.cluster09)
  df.cluster10.grouped <- mpr.group_by_geo(df.cluster10)
}
if (!empty_nodes) mpr.draw_map(spain, df.cluster01.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster02.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster03.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster04.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster05.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster06.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster07.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster08.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster09.grouped)

if (!empty_nodes) mpr.draw_map(spain, df.cluster10.grouped)

LS0tCnRpdGxlOiAiQW7DoWxpc2lzIGRlIG1vZGVsb3MgU09NIC0gRnJlY3VlbmNpYSBkYXRvcyBkZSBlbnRyYWRhOiBtZXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgTW9kZWxvCgoqIElEOiAzNDUKKiBEZXNjcmlwY2nDs246IAoqIEZyZWN1ZW5jaWE6IG1lcwoqIFZhcmlhYmxlczogZmVjaGFfY250LCBwcmVjaXAKKiBEaW1lbnNpb25lcyBkZWwgbWFwYTogNSw1CiogSXRlcmFjaW9uZXM6IDEwMDAKKiBQYXLDoW1ldHJvcyBhZGljaW9uYWxlczogCgpgYGB7cn0Kc291cmNlKCIuLi8uLi9saWIvc29tLXV0aWxzLlIiKQpzb3VyY2UoIi4uLy4uL2xpYi9tYXBzLXV0aWxzLlIiKQpgYGAKCiMgQ2FyZ2EgZGVsIG1vZGVsbyBkZXNkZSBkaXNjbwoKYGBge3J9Cm1wci5zZXRfYmFzZV9wYXRoX2FuYWx5c2lzKCkKbW9kZWwgPC0gbXByLmxvYWRfbW9kZWwoInNvbS0zNDUucmRzLnh6IikKc3VtbWFyeShtb2RlbCkKYGBgCgpgYGB7cn0KcGxvdChtb2RlbCwgdHlwZT0iY2hhbmdlcyIpCmBgYAoKIyBDYXJnYSBkZWwgZGF0YXNldCBkZSBlbnRyYWRhCgpgYGB7cn0KZGYgPC0gbXByLmxvYWRfZGF0YSgiZGF0b3NfbWVzLmNzdi54eiIpCmBgYAoKYGBge3J9CmRmCmBgYAoKYGBge3J9CnN1bW1hcnkoZGYpCmBgYAoKIyBDYXJnYSBkZSBsb3MgbWFwYXMKCmBgYHtyfQp3b3JsZCA8LSBuZV9jb3VudHJpZXMoc2NhbGUgPSAibWVkaXVtIiwgcmV0dXJuY2xhc3MgPSAic2YiKQpzcGFpbiA8LSBzdWJzZXQod29ybGQsIGFkbWluID09ICJTcGFpbiIpCmBgYAoKIyBNYXBhIGRlIGRlbnNpZGFkCgpgYGB7cn0KcGxvdChtb2RlbCwgdHlwZT0iY291bnQiLCBzaGFwZSA9ICJzdHJhaWdodCIsIHBhbGV0dGUubmFtZSA9IG1wci5kZWdyYWRlLmJsZXUpCmBgYAoKTsO6bWVybyBkZSBlbGVtZW50b3MgZW4gY2FkYSBjZWxkYToKCmBgYHtyfQpuYiA8LSB0YWJsZShtb2RlbCR1bml0LmNsYXNzaWYpCnByaW50KG5iKQpgYGAKQ29tcHJvYmFjacOzbiBkZSBub2RvcyB2YWPDrW9zOgoKYGBge3J9CmRpbV9tb2RlbCA8LSA1KjU7Cmxlbl9uYiA9IGxlbmd0aChuYik7CmVtcHR5X25vZGVzIDwtIGRpbV9tb2RlbCAhPSBsZW5fbmI7CmlmIChlbXB0eV9ub2RlcykgewogIHByaW50KHBhc3RlKCJbV2FybmluZ10gRXhpc3RlbiBub2RvcyB2YWPDrW9zOiAiLCBsZW5fbmIsICIvIiwgZGltX21vZGVsKSkKfQpgYGAKCiMgTWFwYSBkZSBkaXN0YW5jaWEgZW50cmUgdmVjaW5vcwoKYGBge3J9CnBsb3QobW9kZWwsIHR5cGU9ImRpc3QubmVpZ2hib3VycyIsIHNoYXBlID0gInN0cmFpZ2h0IikKYGBgCgojIEluZmx1ZW5jaWEgZGUgbGFzIHZhcmlhYmxlcwoKYGBge3J9Cm1vZGVsX2NvbG5hbWVzID0gYygiZmVjaGFfY250IiwgInByZWNpcCIpCm1vZGVsX25jb2wgPSBsZW5ndGgobW9kZWxfY29sbmFtZXMpCmBgYAoKIyMgTWFwYSBkZSB2YXJpYWJsZXMuCgpgYGB7cn0KcGxvdChtb2RlbCwgc2hhcGUgPSAic3RyYWlnaHQiKQpgYGAKCiMjIE1hcGEgZGUgY2Fsb3IgcG9yIHZhcmlhYmxlCgpgYGB7cn0KcGFyKG1mcm93PWMoMyw0KSkKZm9yIChqIGluIDE6bW9kZWxfbmNvbCkgewogIHBsb3QobW9kZWwsIHR5cGU9InByb3BlcnR5IiwgcHJvcGVydHk9Z2V0Q29kZXMobW9kZWwsMSlbLGpdLAogICAgcGFsZXR0ZS5uYW1lPW1wci5jb29sQmx1ZUhvdFJlZCwKICAgIG1haW49bW9kZWxfY29sbmFtZXNbal0sCiAgICBjZXg9MC41LCBzaGFwZSA9ICJzdHJhaWdodCIpCn0KYGBgCgojIyBDb3JyZWxhY2nDs24gcGFyYSBjYWRhIGNvbHVtbmEgZGVsIHZlY3RvciBkZSBub2RvcwoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICBjb3IgPC0gYXBwbHkoZ2V0Q29kZXMobW9kZWwsMSksIDIsIG1wci53ZWlnaHRlZC5jb3JyZWxhdGlvbiwgdz1uYiwgc29tPW1vZGVsKQogIHByaW50KGNvcikKfQpgYGAKClJlcHJlc2VudGFjacOzbiBkZSBjYWRhIHZhcmlhYmxlIGVuIHVuIG1hcGEgZGUgZmFjdG9yZXM6CgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIHBhcihtZnJvdz1jKDEsMSkpCiAgcGxvdChjb3JbMSxdLCBjb3JbMixdLCB4bGltPWMoLTEsMSksIHlsaW09YygtMSwxKSwgdHlwZT0ibiIpCiAgbGluZXMoYygtMSwxKSxjKDAsMCkpCiAgbGluZXMoYygwLDApLGMoLTEsMSkpCiAgdGV4dChjb3JbMSxdLCBjb3JbMixdLCBsYWJlbHM9bW9kZWxfY29sbmFtZXMsIGNleD0wLjc1KQogIHN5bWJvbHMoMCwwLGNpcmNsZXM9MSxpbmNoZXM9RixhZGQ9VCkKfQpgYGAKCkltcG9ydGFuY2lhIGRlIGNhZGEgdmFyaWFibGUgLSB2YXJpYW56YSBwb25kZXJhZGEgcG9yIGVsIHRhbWHDsW8gZGUgbGEgY2VsZGE6CgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIHNpZ21hMiA8LSBzcXJ0KGFwcGx5KGdldENvZGVzKG1vZGVsLDEpLDIsZnVuY3Rpb24oeCxlZmZlY3RpZikKICAgICB7bTwtc3VtKGVmZmVjdGlmKih4LXdlaWdodGVkLm1lYW4oeCxlZmZlY3RpZikpXjIpLyhzdW0oZWZmZWN0aWYpLTEpfSwKICAgICBlZmZlY3RpZj1uYikpCiAgcHJpbnQoc29ydChzaWdtYTIsZGVjcmVhc2luZz1UKSkKfQpgYGAKCiMgQ2x1c3RlcmluZwoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICBoYWMgPC0gbXByLmhhYyhtb2RlbCwgbmIpCn0KYGBgCgojIyBWaXN1YWxpemFjacOzbiBkZSAzIGNsw7pzdGVyZXM6CgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIHBsb3QoaGFjLCBoYW5nPS0xLCBsYWJlbHM9RikKICByZWN0LmhjbHVzdChoYWMsIGs9MykKfQpgYGAKCiMjIyBWaXN1YWxpemFjacOzbiBkZSBsb3MgY2zDunN0ZXJzIGVuIGVsIG1hcGEKCkEgcXXDqSBjbMO6c3RlciBwZXJ0ZW5lY2UgY2FkYSBub2RvIGRlbCBtYXBhIGRlIGtvaG9uZW46CgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGdyb3VwcyA8LSBjdXRyZWUoaGFjLCBrPTMpCiAgcGxvdChtb2RlbCwgdHlwZT0ibWFwcGluZyIsCiAgICBiZ2NvbD1jKCJzdGVlbGJsdWUxIiwic2llbm5hMSIsInllbGxvd2dyZWVuIiwicmVkIiwiYmx1ZSIsInllbGxvdyIsInB1cnBsZSIsImdyZWVuIiwid2hpdGUiLCIjMWY3N2I0IiwgJyNmZjdmMGUnLCAnIzJjYTAyYycsICcjZDYyNzI4JywgJyM5NDY3YmQnLCAnIzhjNTY0YicsICcjZTM3N2MyJylbZ3JvdXBzXSwKICAgIHNoYXBlID0gInN0cmFpZ2h0IiwgbGFiZWxzID0gIiIpCiAgYWRkLmNsdXN0ZXIuYm91bmRhcmllcyhtb2RlbCwgY2x1c3RlcmluZz1ncm91cHMpCn0KYGBgCgojIyMgQW7DoWxpc2lzIGRlIGxhcyBvYnNlcnZhY2lvbmVzIGRlIGNhZGEgY2x1c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICAjIEFzaWduYW1vcyBhIGNhZGEgcmVnaXN0cm8gc3UgY2zDunN0ZXIKICBkZiRjbHVzdGVyIDwtIGdyb3Vwc1ttb2RlbCR1bml0LmNsYXNzaWZdCn0KYGBgCgpOdWV2b3MgZGF0YWZyYW1lcyBwb3IgY2x1c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICAjIENyZW8gbnVldm9zIGRhdGFmcmFtZXMsIHVubyBwb3IgY2FkYSBjbMO6c3Rlci4KICBkZi5jbHVzdGVyMDEgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT0xKQogIGRmLmNsdXN0ZXIwMiA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTIpCiAgZGYuY2x1c3RlcjAzIDwtIHN1YnNldChkZiwgY2x1c3Rlcj09MykKCiAgIyBFeHRyYWlnbyBkZWwgZGF0YWZyYW1lIGxhcyBmZWF0dXJlcy4KICBkZi5jbHVzdGVyMDEgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwMSwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjAyIDwtIHNlbGVjdChkZi5jbHVzdGVyMDIsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwMyA8LSBzZWxlY3QoZGYuY2x1c3RlcjAzLCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKfQpgYGAKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwMSkKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjAzKQpgYGAKCiMjIyMgTsO6bWVybyBkZSBlbGVtZW50b3MgZW4gY2FkYSBjbMO6c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICBkZi5jbHVzdGVycy5kaW0gPC0gYyhkaW0oZGYuY2x1c3RlcjAxKVsxXSwgZGltKGRmLmNsdXN0ZXIwMilbMV0sIGRpbShkZi5jbHVzdGVyMDMpWzFdKQogIGJhcnBsb3QoZGYuY2x1c3RlcnMuZGltLAogICAgICAgICAgbmFtZXMuYXJnID0gYygiY2x1c3RlcjAxIiwgImNsdXN0ZXIwMiIsICJjbHVzdGVyMDMiKSwKICAgICAgICAgIGNvbCA9ICJzdGVlbGJsdWUxIikKfQpgYGAKCiMjIyMgRGlzdHJpYnVjacOzbiBkZSBsb3MgZGF0b3MKCmBgYHtyIGZpZy5oZWlnaHQ9N30KaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwMykKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTV9CmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwMSkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjAyKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDMpCmBgYAoKIyMjIExvY2FsaXphY2nDs24gZ2VvZ3LDoWZpY2EgZGUgbGFzIGVzdGFjaW9uZXMgZGUgbWVkaWRhIGRlIGNhZGEgY2x1c3RlcgoKYGBge3J9CiMgQWdydXBhIHBvciBsb25naXR1ZCB5IGxhdGl0dWQgcGFyYSByZWxsZW5hciBlbCBtYXBhIGNvbiBtZW5vcyBkYXRvcy4KaWYgKCFlbXB0eV9ub2RlcykgewogIGRmLmNsdXN0ZXIwMS5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjAxKQogIGRmLmNsdXN0ZXIwMi5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjAyKQogIGRmLmNsdXN0ZXIwMy5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjAzKQp9CmBgYAoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjAxLmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjAyLmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjAzLmdyb3VwZWQpCmBgYAoKIyMgVmlzdWFsaXphY2nDs24gZGUgNCBjbMO6c3RlcmVzOgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICBwbG90KGhhYywgaGFuZz0tMSwgbGFiZWxzPUYpCiAgcmVjdC5oY2x1c3QoaGFjLCBrPTQpCn0KYGBgCgojIyMgVmlzdWFsaXphY2nDs24gZGUgbG9zIGNsw7pzdGVycyBlbiBlbCBtYXBhCgpBIHF1w6kgY2zDunN0ZXIgcGVydGVuZWNlIGNhZGEgbm9kbyBkZWwgbWFwYSBkZSBrb2hvbmVuOgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICBncm91cHMgPC0gY3V0cmVlKGhhYywgaz00KQogIHBsb3QobW9kZWwsIHR5cGU9Im1hcHBpbmciLAogICAgYmdjb2w9Yygic3RlZWxibHVlMSIsInNpZW5uYTEiLCJ5ZWxsb3dncmVlbiIsInJlZCIsImJsdWUiLCJ5ZWxsb3ciLCJwdXJwbGUiLCJncmVlbiIsIndoaXRlIiwiIzFmNzdiNCIsICcjZmY3ZjBlJywgJyMyY2EwMmMnLCAnI2Q2MjcyOCcsICcjOTQ2N2JkJywgJyM4YzU2NGInLCAnI2UzNzdjMicpW2dyb3Vwc10sCiAgICBzaGFwZSA9ICJzdHJhaWdodCIsIGxhYmVscyA9ICIiKQogIGFkZC5jbHVzdGVyLmJvdW5kYXJpZXMobW9kZWwsIGNsdXN0ZXJpbmc9Z3JvdXBzKQp9CmBgYAoKIyMjIEFuw6FsaXNpcyBkZSBsYXMgb2JzZXJ2YWNpb25lcyBkZSBjYWRhIGNsdXN0ZXIKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgIyBBc2lnbmFtb3MgYSBjYWRhIHJlZ2lzdHJvIHN1IGNsw7pzdGVyCiAgZGYkY2x1c3RlciA8LSBncm91cHNbbW9kZWwkdW5pdC5jbGFzc2lmXQp9CmBgYAoKTnVldm9zIGRhdGFmcmFtZXMgcG9yIGNsdXN0ZXIKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgIyBDcmVvIG51ZXZvcyBkYXRhZnJhbWVzLCB1bm8gcG9yIGNhZGEgY2zDunN0ZXIuCiAgZGYuY2x1c3RlcjAxIDwtIHN1YnNldChkZiwgY2x1c3Rlcj09MSkKICBkZi5jbHVzdGVyMDIgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT0yKQogIGRmLmNsdXN0ZXIwMyA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTMpCiAgZGYuY2x1c3RlcjA0IDwtIHN1YnNldChkZiwgY2x1c3Rlcj09NCkKCiAgIyBFeHRyYWlnbyBkZWwgZGF0YWZyYW1lIGxhcyBmZWF0dXJlcy4KICBkZi5jbHVzdGVyMDEgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwMSwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjAyIDwtIHNlbGVjdChkZi5jbHVzdGVyMDIsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwMyA8LSBzZWxlY3QoZGYuY2x1c3RlcjAzLCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDQgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwNCwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCn0KYGBgCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDEpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjAyKQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwMykKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDQpCmBgYAoKIyMjIyBOw7ptZXJvIGRlIGVsZW1lbnRvcyBlbiBjYWRhIGNsw7pzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGRmLmNsdXN0ZXJzLmRpbSA8LSBjKGRpbShkZi5jbHVzdGVyMDEpWzFdLCBkaW0oZGYuY2x1c3RlcjAyKVsxXSwgZGltKGRmLmNsdXN0ZXIwMylbMV0sIGRpbShkZi5jbHVzdGVyMDQpWzFdKQogIGJhcnBsb3QoZGYuY2x1c3RlcnMuZGltLAogICAgICAgICAgbmFtZXMuYXJnID0gYygiY2x1c3RlcjAxIiwgImNsdXN0ZXIwMiIsICJjbHVzdGVyMDMiLCAiY2x1c3RlcjA0IiksCiAgICAgICAgICBjb2wgPSAic3RlZWxibHVlMSIpCn0KYGBgCgojIyMjIERpc3RyaWJ1Y2nDs24gZGUgbG9zIGRhdG9zCgpgYGB7ciBmaWcuaGVpZ2h0PTd9CmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwMSkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjAyKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDMpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwNCkKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTV9CmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwMSkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjAyKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDMpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwNCkKYGBgCgojIyMgTG9jYWxpemFjacOzbiBnZW9ncsOhZmljYSBkZSBsYXMgZXN0YWNpb25lcyBkZSBtZWRpZGEgZGUgY2FkYSBjbHVzdGVyCgpgYGB7cn0KIyBBZ3J1cGEgcG9yIGxvbmdpdHVkIHkgbGF0aXR1ZCBwYXJhIHJlbGxlbmFyIGVsIG1hcGEgY29uIG1lbm9zIGRhdG9zLgppZiAoIWVtcHR5X25vZGVzKSB7CiAgZGYuY2x1c3RlcjAxLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDEpCiAgZGYuY2x1c3RlcjAyLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDIpCiAgZGYuY2x1c3RlcjAzLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDMpCiAgZGYuY2x1c3RlcjA0Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDQpCn0KYGBgCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDEuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDIuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDMuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDQuZ3JvdXBlZCkKYGBgCgojIyBWaXN1YWxpemFjacOzbiBkZSA1IGNsw7pzdGVyZXM6CgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIHBsb3QoaGFjLCBoYW5nPS0xLCBsYWJlbHM9RikKICByZWN0LmhjbHVzdChoYWMsIGs9NSkKfQpgYGAKCiMjIyBWaXN1YWxpemFjacOzbiBkZSBsb3MgY2zDunN0ZXJzIGVuIGVsIG1hcGEKCkEgcXXDqSBjbMO6c3RlciBwZXJ0ZW5lY2UgY2FkYSBub2RvIGRlbCBtYXBhIGRlIGtvaG9uZW46CgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGdyb3VwcyA8LSBjdXRyZWUoaGFjLCBrPTUpCiAgcGxvdChtb2RlbCwgdHlwZT0ibWFwcGluZyIsCiAgICBiZ2NvbD1jKCJzdGVlbGJsdWUxIiwic2llbm5hMSIsInllbGxvd2dyZWVuIiwicmVkIiwiYmx1ZSIsInllbGxvdyIsInB1cnBsZSIsImdyZWVuIiwid2hpdGUiLCIjMWY3N2I0IiwgJyNmZjdmMGUnLCAnIzJjYTAyYycsICcjZDYyNzI4JywgJyM5NDY3YmQnLCAnIzhjNTY0YicsICcjZTM3N2MyJylbZ3JvdXBzXSwKICAgIHNoYXBlID0gInN0cmFpZ2h0IiwgbGFiZWxzID0gIiIpCiAgYWRkLmNsdXN0ZXIuYm91bmRhcmllcyhtb2RlbCwgY2x1c3RlcmluZz1ncm91cHMpCn0KYGBgCgojIyMgQW7DoWxpc2lzIGRlIGxhcyBvYnNlcnZhY2lvbmVzIGRlIGNhZGEgY2x1c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICAjIEFzaWduYW1vcyBhIGNhZGEgcmVnaXN0cm8gc3UgY2zDunN0ZXIKICBkZiRjbHVzdGVyIDwtIGdyb3Vwc1ttb2RlbCR1bml0LmNsYXNzaWZdCn0KYGBgCgpOdWV2b3MgZGF0YWZyYW1lcyBwb3IgY2x1c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICAjIENyZW8gbnVldm9zIGRhdGFmcmFtZXMsIHVubyBwb3IgY2FkYSBjbMO6c3Rlci4KICBkZi5jbHVzdGVyMDEgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT0xKQogIGRmLmNsdXN0ZXIwMiA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTIpCiAgZGYuY2x1c3RlcjAzIDwtIHN1YnNldChkZiwgY2x1c3Rlcj09MykKICBkZi5jbHVzdGVyMDQgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT00KQogIGRmLmNsdXN0ZXIwNSA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTUpCgogICMgRXh0cmFpZ28gZGVsIGRhdGFmcmFtZSBsYXMgZmVhdHVyZXMuCiAgZGYuY2x1c3RlcjAxIDwtIHNlbGVjdChkZi5jbHVzdGVyMDEsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwMiA8LSBzZWxlY3QoZGYuY2x1c3RlcjAyLCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDMgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwMywgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjA0IDwtIHNlbGVjdChkZi5jbHVzdGVyMDQsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwNSA8LSBzZWxlY3QoZGYuY2x1c3RlcjA1LCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKfQpgYGAKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwMSkKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjAzKQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwNCkKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDUpCmBgYAoKIyMjIyBOw7ptZXJvIGRlIGVsZW1lbnRvcyBlbiBjYWRhIGNsw7pzdGVyCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGRmLmNsdXN0ZXJzLmRpbSA8LSBjKGRpbShkZi5jbHVzdGVyMDEpWzFdLCBkaW0oZGYuY2x1c3RlcjAyKVsxXSwgZGltKGRmLmNsdXN0ZXIwMylbMV0sIGRpbShkZi5jbHVzdGVyMDQpWzFdLCBkaW0oZGYuY2x1c3RlcjA1KVsxXSkKICBiYXJwbG90KGRmLmNsdXN0ZXJzLmRpbSwKICAgICAgICAgIG5hbWVzLmFyZyA9IGMoImNsdXN0ZXIwMSIsICJjbHVzdGVyMDIiLCAiY2x1c3RlcjAzIiwgImNsdXN0ZXIwNCIsICJjbHVzdGVyMDUiKSwKICAgICAgICAgIGNvbCA9ICJzdGVlbGJsdWUxIikKfQpgYGAKCiMjIyMgRGlzdHJpYnVjacOzbiBkZSBsb3MgZGF0b3MKCmBgYHtyIGZpZy5oZWlnaHQ9N30KaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwMykKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjA0KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDUpCmBgYAoKYGBge3IgZmlnLmhlaWdodD01fQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDEpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwMikKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjAzKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwNSkKYGBgCgojIyMgTG9jYWxpemFjacOzbiBnZW9ncsOhZmljYSBkZSBsYXMgZXN0YWNpb25lcyBkZSBtZWRpZGEgZGUgY2FkYSBjbHVzdGVyCgpgYGB7cn0KIyBBZ3J1cGEgcG9yIGxvbmdpdHVkIHkgbGF0aXR1ZCBwYXJhIHJlbGxlbmFyIGVsIG1hcGEgY29uIG1lbm9zIGRhdG9zLgppZiAoIWVtcHR5X25vZGVzKSB7CiAgZGYuY2x1c3RlcjAxLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDEpCiAgZGYuY2x1c3RlcjAyLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDIpCiAgZGYuY2x1c3RlcjAzLmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDMpCiAgZGYuY2x1c3RlcjA0Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDQpCiAgZGYuY2x1c3RlcjA1Lmdyb3VwZWQgPC0gbXByLmdyb3VwX2J5X2dlbyhkZi5jbHVzdGVyMDUpCn0KYGBgCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDEuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDIuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDMuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDQuZ3JvdXBlZCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmRyYXdfbWFwKHNwYWluLCBkZi5jbHVzdGVyMDUuZ3JvdXBlZCkKYGBgCgojIyBWaXN1YWxpemFjacOzbiBkZSA2IGNsw7pzdGVyZXM6CgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIHBsb3QoaGFjLCBoYW5nPS0xLCBsYWJlbHM9RikKICByZWN0LmhjbHVzdChoYWMsIGs9NikKfQpgYGAKCiMjIyBWaXN1YWxpemFjacOzbiBkZSBsb3MgY2zDunN0ZXJzIGVuIGVsIG1hcGEKCkEgcXXDqSBjbMO6c3RlciBwZXJ0ZW5lY2UgY2FkYSBub2RvIGRlbCBtYXBhIGRlIGtvaG9uZW46CgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGdyb3VwcyA8LSBjdXRyZWUoaGFjLCBrPTYpCiAgcGxvdChtb2RlbCwgdHlwZT0ibWFwcGluZyIsCiAgICBiZ2NvbD1jKCJzdGVlbGJsdWUxIiwic2llbm5hMSIsInllbGxvd2dyZWVuIiwicmVkIiwiYmx1ZSIsInllbGxvdyIsInB1cnBsZSIsImdyZWVuIiwid2hpdGUiLCIjMWY3N2I0IiwgJyNmZjdmMGUnLCAnIzJjYTAyYycsICcjZDYyNzI4JywgJyM5NDY3YmQnLCAnIzhjNTY0YicsICcjZTM3N2MyJylbZ3JvdXBzXSwKICAgIHNoYXBlID0gInN0cmFpZ2h0IiwgbGFiZWxzID0gIiIpCiAgYWRkLmNsdXN0ZXIuYm91bmRhcmllcyhtb2RlbCwgY2x1c3RlcmluZz1ncm91cHMpCn0KYGBgCgojIyMgQW7DoWxpc2lzIGRlIGxhcyBvYnNlcnZhY2lvbmVzIGRlIGNhZGEgY2x1c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICAjIEFzaWduYW1vcyBhIGNhZGEgcmVnaXN0cm8gc3UgY2zDunN0ZXIKICBkZiRjbHVzdGVyIDwtIGdyb3Vwc1ttb2RlbCR1bml0LmNsYXNzaWZdCn0KYGBgCgpOdWV2b3MgZGF0YWZyYW1lcyBwb3IgY2x1c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICAjIENyZW8gbnVldm9zIGRhdGFmcmFtZXMsIHVubyBwb3IgY2FkYSBjbMO6c3Rlci4KICBkZi5jbHVzdGVyMDEgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT0xKQogIGRmLmNsdXN0ZXIwMiA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTIpCiAgZGYuY2x1c3RlcjAzIDwtIHN1YnNldChkZiwgY2x1c3Rlcj09MykKICBkZi5jbHVzdGVyMDQgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT00KQogIGRmLmNsdXN0ZXIwNSA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTUpCiAgZGYuY2x1c3RlcjA2IDwtIHN1YnNldChkZiwgY2x1c3Rlcj09NikKCiAgIyBFeHRyYWlnbyBkZWwgZGF0YWZyYW1lIGxhcyBmZWF0dXJlcy4KICBkZi5jbHVzdGVyMDEgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwMSwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjAyIDwtIHNlbGVjdChkZi5jbHVzdGVyMDIsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwMyA8LSBzZWxlY3QoZGYuY2x1c3RlcjAzLCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDQgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwNCwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjA1IDwtIHNlbGVjdChkZi5jbHVzdGVyMDUsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwNiA8LSBzZWxlY3QoZGYuY2x1c3RlcjA2LCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKfQpgYGAKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwMSkKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjAzKQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwNCkKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDUpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjA2KQpgYGAKCiMjIyMgTsO6bWVybyBkZSBlbGVtZW50b3MgZW4gY2FkYSBjbMO6c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICBkZi5jbHVzdGVycy5kaW0gPC0gYyhkaW0oZGYuY2x1c3RlcjAxKVsxXSwgZGltKGRmLmNsdXN0ZXIwMilbMV0sIGRpbShkZi5jbHVzdGVyMDMpWzFdLCBkaW0oZGYuY2x1c3RlcjA0KVsxXSwgZGltKGRmLmNsdXN0ZXIwNSlbMV0sIGRpbShkZi5jbHVzdGVyMDYpWzFdKQogIGJhcnBsb3QoZGYuY2x1c3RlcnMuZGltLAogICAgICAgICAgbmFtZXMuYXJnID0gYygiY2x1c3RlcjAxIiwgImNsdXN0ZXIwMiIsICJjbHVzdGVyMDMiLCAiY2x1c3RlcjA0IiwgImNsdXN0ZXIwNSIsICJjbHVzdGVyMDYiKSwKICAgICAgICAgIGNvbCA9ICJzdGVlbGJsdWUxIikKfQpgYGAKCiMjIyMgRGlzdHJpYnVjacOzbiBkZSBsb3MgZGF0b3MKCmBgYHtyIGZpZy5oZWlnaHQ9N30KaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwMykKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjA0KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDUpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwNikKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTV9CmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwMSkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjAyKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDMpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwNCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjA1KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDYpCmBgYAoKIyMjIExvY2FsaXphY2nDs24gZ2VvZ3LDoWZpY2EgZGUgbGFzIGVzdGFjaW9uZXMgZGUgbWVkaWRhIGRlIGNhZGEgY2x1c3RlcgoKYGBge3J9CiMgQWdydXBhIHBvciBsb25naXR1ZCB5IGxhdGl0dWQgcGFyYSByZWxsZW5hciBlbCBtYXBhIGNvbiBtZW5vcyBkYXRvcy4KaWYgKCFlbXB0eV9ub2RlcykgewogIGRmLmNsdXN0ZXIwMS5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjAxKQogIGRmLmNsdXN0ZXIwMi5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjAyKQogIGRmLmNsdXN0ZXIwMy5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjAzKQogIGRmLmNsdXN0ZXIwNC5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjA0KQogIGRmLmNsdXN0ZXIwNS5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjA1KQogIGRmLmNsdXN0ZXIwNi5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjA2KQp9CmBgYAoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjAxLmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjAyLmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjAzLmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjA0Lmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjA1Lmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjA2Lmdyb3VwZWQpCmBgYAoKIyMgVmlzdWFsaXphY2nDs24gZGUgOCBjbMO6c3RlcmVzOgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICBwbG90KGhhYywgaGFuZz0tMSwgbGFiZWxzPUYpCiAgcmVjdC5oY2x1c3QoaGFjLCBrPTgpCn0KYGBgCgojIyMgVmlzdWFsaXphY2nDs24gZGUgbG9zIGNsw7pzdGVycyBlbiBlbCBtYXBhCgpBIHF1w6kgY2zDunN0ZXIgcGVydGVuZWNlIGNhZGEgbm9kbyBkZWwgbWFwYSBkZSBrb2hvbmVuOgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICBncm91cHMgPC0gY3V0cmVlKGhhYywgaz04KQogIHBsb3QobW9kZWwsIHR5cGU9Im1hcHBpbmciLAogICAgYmdjb2w9Yygic3RlZWxibHVlMSIsInNpZW5uYTEiLCJ5ZWxsb3dncmVlbiIsInJlZCIsImJsdWUiLCJ5ZWxsb3ciLCJwdXJwbGUiLCJncmVlbiIsIndoaXRlIiwiIzFmNzdiNCIsICcjZmY3ZjBlJywgJyMyY2EwMmMnLCAnI2Q2MjcyOCcsICcjOTQ2N2JkJywgJyM4YzU2NGInLCAnI2UzNzdjMicpW2dyb3Vwc10sCiAgICBzaGFwZSA9ICJzdHJhaWdodCIsIGxhYmVscyA9ICIiKQogIGFkZC5jbHVzdGVyLmJvdW5kYXJpZXMobW9kZWwsIGNsdXN0ZXJpbmc9Z3JvdXBzKQp9CmBgYAoKIyMjIEFuw6FsaXNpcyBkZSBsYXMgb2JzZXJ2YWNpb25lcyBkZSBjYWRhIGNsdXN0ZXIKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgIyBBc2lnbmFtb3MgYSBjYWRhIHJlZ2lzdHJvIHN1IGNsw7pzdGVyCiAgZGYkY2x1c3RlciA8LSBncm91cHNbbW9kZWwkdW5pdC5jbGFzc2lmXQp9CmBgYAoKTnVldm9zIGRhdGFmcmFtZXMgcG9yIGNsdXN0ZXIKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgIyBDcmVvIG51ZXZvcyBkYXRhZnJhbWVzLCB1bm8gcG9yIGNhZGEgY2zDunN0ZXIuCiAgZGYuY2x1c3RlcjAxIDwtIHN1YnNldChkZiwgY2x1c3Rlcj09MSkKICBkZi5jbHVzdGVyMDIgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT0yKQogIGRmLmNsdXN0ZXIwMyA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTMpCiAgZGYuY2x1c3RlcjA0IDwtIHN1YnNldChkZiwgY2x1c3Rlcj09NCkKICBkZi5jbHVzdGVyMDUgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT01KQogIGRmLmNsdXN0ZXIwNiA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTYpCiAgZGYuY2x1c3RlcjA3IDwtIHN1YnNldChkZiwgY2x1c3Rlcj09NykKICBkZi5jbHVzdGVyMDggPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT04KQoKICAjIEV4dHJhaWdvIGRlbCBkYXRhZnJhbWUgbGFzIGZlYXR1cmVzLgogIGRmLmNsdXN0ZXIwMSA8LSBzZWxlY3QoZGYuY2x1c3RlcjAxLCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDIgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwMiwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjAzIDwtIHNlbGVjdChkZi5jbHVzdGVyMDMsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwNCA8LSBzZWxlY3QoZGYuY2x1c3RlcjA0LCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDUgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwNSwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjA2IDwtIHNlbGVjdChkZi5jbHVzdGVyMDYsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwNyA8LSBzZWxlY3QoZGYuY2x1c3RlcjA3LCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDggPC0gc2VsZWN0KGRmLmNsdXN0ZXIwOCwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCn0KYGBgCgpgYGB7cn0KaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDEpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjAyKQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwMykKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDQpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjA1KQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwNikKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDcpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjA4KQpgYGAKCiMjIyMgTsO6bWVybyBkZSBlbGVtZW50b3MgZW4gY2FkYSBjbMO6c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICBkZi5jbHVzdGVycy5kaW0gPC0gYyhkaW0oZGYuY2x1c3RlcjAxKVsxXSwgZGltKGRmLmNsdXN0ZXIwMilbMV0sIGRpbShkZi5jbHVzdGVyMDMpWzFdLCBkaW0oZGYuY2x1c3RlcjA0KVsxXSwgZGltKGRmLmNsdXN0ZXIwNSlbMV0sIGRpbShkZi5jbHVzdGVyMDYpWzFdLCBkaW0oZGYuY2x1c3RlcjA3KVsxXSwgZGltKGRmLmNsdXN0ZXIwOClbMV0pCiAgYmFycGxvdChkZi5jbHVzdGVycy5kaW0sCiAgICAgICAgICBuYW1lcy5hcmcgPSBjKCJjbHVzdGVyMDEiLCAiY2x1c3RlcjAyIiwgImNsdXN0ZXIwMyIsICJjbHVzdGVyMDQiLCAiY2x1c3RlcjA1IiwgImNsdXN0ZXIwNiIsICJjbHVzdGVyMDciLCAiY2x1c3RlcjA4IiksCiAgICAgICAgICBjb2wgPSAic3RlZWxibHVlMSIpCn0KYGBgCgojIyMjIERpc3RyaWJ1Y2nDs24gZGUgbG9zIGRhdG9zCgpgYGB7ciBmaWcuaGVpZ2h0PTd9CmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwMSkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjAyKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDMpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwNCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjA1KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDYpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwNykKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjA4KQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9NX0KaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDIpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwMykKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjA0KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDUpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwNikKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjA3KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDgpCmBgYAoKIyMjIExvY2FsaXphY2nDs24gZ2VvZ3LDoWZpY2EgZGUgbGFzIGVzdGFjaW9uZXMgZGUgbWVkaWRhIGRlIGNhZGEgY2x1c3RlcgoKYGBge3J9CiMgQWdydXBhIHBvciBsb25naXR1ZCB5IGxhdGl0dWQgcGFyYSByZWxsZW5hciBlbCBtYXBhIGNvbiBtZW5vcyBkYXRvcy4KaWYgKCFlbXB0eV9ub2RlcykgewogIGRmLmNsdXN0ZXIwMS5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjAxKQogIGRmLmNsdXN0ZXIwMi5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjAyKQogIGRmLmNsdXN0ZXIwMy5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjAzKQogIGRmLmNsdXN0ZXIwNC5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjA0KQogIGRmLmNsdXN0ZXIwNS5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjA1KQogIGRmLmNsdXN0ZXIwNi5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjA2KQogIGRmLmNsdXN0ZXIwNy5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjA3KQogIGRmLmNsdXN0ZXIwOC5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjA4KQp9CmBgYAoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjAxLmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjAyLmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjAzLmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjA0Lmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjA1Lmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjA2Lmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjA3Lmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjA4Lmdyb3VwZWQpCmBgYAoKIyMgVmlzdWFsaXphY2nDs24gZGUgMTAgY2zDunN0ZXJlczoKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgcGxvdChoYWMsIGhhbmc9LTEsIGxhYmVscz1GKQogIHJlY3QuaGNsdXN0KGhhYywgaz0xMCkKfQpgYGAKCiMjIyBWaXN1YWxpemFjacOzbiBkZSBsb3MgY2zDunN0ZXJzIGVuIGVsIG1hcGEKCkEgcXXDqSBjbMO6c3RlciBwZXJ0ZW5lY2UgY2FkYSBub2RvIGRlbCBtYXBhIGRlIGtvaG9uZW46CgpgYGB7cn0KaWYgKCFlbXB0eV9ub2RlcykgewogIGdyb3VwcyA8LSBjdXRyZWUoaGFjLCBrPTEwKQogIHBsb3QobW9kZWwsIHR5cGU9Im1hcHBpbmciLAogICAgYmdjb2w9Yygic3RlZWxibHVlMSIsInNpZW5uYTEiLCJ5ZWxsb3dncmVlbiIsInJlZCIsImJsdWUiLCJ5ZWxsb3ciLCJwdXJwbGUiLCJncmVlbiIsIndoaXRlIiwiIzFmNzdiNCIsICcjZmY3ZjBlJywgJyMyY2EwMmMnLCAnI2Q2MjcyOCcsICcjOTQ2N2JkJywgJyM4YzU2NGInLCAnI2UzNzdjMicpW2dyb3Vwc10sCiAgICBzaGFwZSA9ICJzdHJhaWdodCIsIGxhYmVscyA9ICIiKQogIGFkZC5jbHVzdGVyLmJvdW5kYXJpZXMobW9kZWwsIGNsdXN0ZXJpbmc9Z3JvdXBzKQp9CmBgYAoKIyMjIEFuw6FsaXNpcyBkZSBsYXMgb2JzZXJ2YWNpb25lcyBkZSBjYWRhIGNsdXN0ZXIKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgIyBBc2lnbmFtb3MgYSBjYWRhIHJlZ2lzdHJvIHN1IGNsw7pzdGVyCiAgZGYkY2x1c3RlciA8LSBncm91cHNbbW9kZWwkdW5pdC5jbGFzc2lmXQp9CmBgYAoKTnVldm9zIGRhdGFmcmFtZXMgcG9yIGNsdXN0ZXIKCmBgYHtyfQppZiAoIWVtcHR5X25vZGVzKSB7CiAgIyBDcmVvIG51ZXZvcyBkYXRhZnJhbWVzLCB1bm8gcG9yIGNhZGEgY2zDunN0ZXIuCiAgZGYuY2x1c3RlcjAxIDwtIHN1YnNldChkZiwgY2x1c3Rlcj09MSkKICBkZi5jbHVzdGVyMDIgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT0yKQogIGRmLmNsdXN0ZXIwMyA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTMpCiAgZGYuY2x1c3RlcjA0IDwtIHN1YnNldChkZiwgY2x1c3Rlcj09NCkKICBkZi5jbHVzdGVyMDUgPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT01KQogIGRmLmNsdXN0ZXIwNiA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTYpCiAgZGYuY2x1c3RlcjA3IDwtIHN1YnNldChkZiwgY2x1c3Rlcj09NykKICBkZi5jbHVzdGVyMDggPC0gc3Vic2V0KGRmLCBjbHVzdGVyPT04KQogIGRmLmNsdXN0ZXIwOSA8LSBzdWJzZXQoZGYsIGNsdXN0ZXI9PTkpCiAgZGYuY2x1c3RlcjEwIDwtIHN1YnNldChkZiwgY2x1c3Rlcj09MTApCgogICMgRXh0cmFpZ28gZGVsIGRhdGFmcmFtZSBsYXMgZmVhdHVyZXMuCiAgZGYuY2x1c3RlcjAxIDwtIHNlbGVjdChkZi5jbHVzdGVyMDEsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwMiA8LSBzZWxlY3QoZGYuY2x1c3RlcjAyLCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDMgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwMywgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjA0IDwtIHNlbGVjdChkZi5jbHVzdGVyMDQsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwNSA8LSBzZWxlY3QoZGYuY2x1c3RlcjA1LCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDYgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwNiwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjA3IDwtIHNlbGVjdChkZi5jbHVzdGVyMDcsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQogIGRmLmNsdXN0ZXIwOCA8LSBzZWxlY3QoZGYuY2x1c3RlcjA4LCBmZWNoYV9jbnQsIHRtYXgsIHRtaW4sIHByZWNpcCwgbmV2YWRhLCBwcm9mX25pZXZlLCBsb25naXR1ZCwgbGF0aXR1ZCwgYWx0aXR1ZCkKICBkZi5jbHVzdGVyMDkgPC0gc2VsZWN0KGRmLmNsdXN0ZXIwOSwgZmVjaGFfY250LCB0bWF4LCB0bWluLCBwcmVjaXAsIG5ldmFkYSwgcHJvZl9uaWV2ZSwgbG9uZ2l0dWQsIGxhdGl0dWQsIGFsdGl0dWQpCiAgZGYuY2x1c3RlcjEwIDwtIHNlbGVjdChkZi5jbHVzdGVyMTAsIGZlY2hhX2NudCwgdG1heCwgdG1pbiwgcHJlY2lwLCBuZXZhZGEsIHByb2ZfbmlldmUsIGxvbmdpdHVkLCBsYXRpdHVkLCBhbHRpdHVkKQp9CmBgYAoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjAxKQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwMikKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDMpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjA0KQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwNSkKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDYpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjA3KQppZiAoIWVtcHR5X25vZGVzKSBzdW1tYXJ5KGRmLmNsdXN0ZXIwOCkKaWYgKCFlbXB0eV9ub2Rlcykgc3VtbWFyeShkZi5jbHVzdGVyMDkpCmlmICghZW1wdHlfbm9kZXMpIHN1bW1hcnkoZGYuY2x1c3RlcjEwKQpgYGAKCiMjIyMgTsO6bWVybyBkZSBlbGVtZW50b3MgZW4gY2FkYSBjbMO6c3RlcgoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIHsKICBkZi5jbHVzdGVycy5kaW0gPC0gYyhkaW0oZGYuY2x1c3RlcjAxKVsxXSwgZGltKGRmLmNsdXN0ZXIwMilbMV0sIGRpbShkZi5jbHVzdGVyMDMpWzFdLCBkaW0oZGYuY2x1c3RlcjA0KVsxXSwgZGltKGRmLmNsdXN0ZXIwNSlbMV0sIGRpbShkZi5jbHVzdGVyMDYpWzFdLCBkaW0oZGYuY2x1c3RlcjA3KVsxXSwgZGltKGRmLmNsdXN0ZXIwOClbMV0sIGRpbShkZi5jbHVzdGVyMDkpWzFdLCBkaW0oZGYuY2x1c3RlcjEwKVsxXSkKICBiYXJwbG90KGRmLmNsdXN0ZXJzLmRpbSwKICAgICAgICAgIG5hbWVzLmFyZyA9IGMoImNsdXN0ZXIwMSIsICJjbHVzdGVyMDIiLCAiY2x1c3RlcjAzIiwgImNsdXN0ZXIwNCIsICJjbHVzdGVyMDUiLCAiY2x1c3RlcjA2IiwgImNsdXN0ZXIwNyIsICJjbHVzdGVyMDgiLCAiY2x1c3RlcjA5IiwgImNsdXN0ZXIxMCIpLAogICAgICAgICAgY29sID0gInN0ZWVsYmx1ZTEiKQp9CmBgYAoKIyMjIyBEaXN0cmlidWNpw7NuIGRlIGxvcyBkYXRvcwoKYGBge3IgZmlnLmhlaWdodD03fQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDEpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwMikKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjAzKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwNSkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjA2KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMDcpCmlmICghZW1wdHlfbm9kZXMpIG1wci5oaXN0KGRmLmNsdXN0ZXIwOCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmhpc3QoZGYuY2x1c3RlcjA5KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuaGlzdChkZi5jbHVzdGVyMTApCmBgYAoKYGBge3IgZmlnLmhlaWdodD01fQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDEpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwMikKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjAzKQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwNSkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjA2KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMDcpCmlmICghZW1wdHlfbm9kZXMpIG1wci5ib3hwbG90KGRmLmNsdXN0ZXIwOCkKaWYgKCFlbXB0eV9ub2RlcykgbXByLmJveHBsb3QoZGYuY2x1c3RlcjA5KQppZiAoIWVtcHR5X25vZGVzKSBtcHIuYm94cGxvdChkZi5jbHVzdGVyMTApCmBgYAoKIyMjIExvY2FsaXphY2nDs24gZ2VvZ3LDoWZpY2EgZGUgbGFzIGVzdGFjaW9uZXMgZGUgbWVkaWRhIGRlIGNhZGEgY2x1c3RlcgoKYGBge3J9CiMgQWdydXBhIHBvciBsb25naXR1ZCB5IGxhdGl0dWQgcGFyYSByZWxsZW5hciBlbCBtYXBhIGNvbiBtZW5vcyBkYXRvcy4KaWYgKCFlbXB0eV9ub2RlcykgewogIGRmLmNsdXN0ZXIwMS5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjAxKQogIGRmLmNsdXN0ZXIwMi5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjAyKQogIGRmLmNsdXN0ZXIwMy5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjAzKQogIGRmLmNsdXN0ZXIwNC5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjA0KQogIGRmLmNsdXN0ZXIwNS5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjA1KQogIGRmLmNsdXN0ZXIwNi5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjA2KQogIGRmLmNsdXN0ZXIwNy5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjA3KQogIGRmLmNsdXN0ZXIwOC5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjA4KQogIGRmLmNsdXN0ZXIwOS5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjA5KQogIGRmLmNsdXN0ZXIxMC5ncm91cGVkIDwtIG1wci5ncm91cF9ieV9nZW8oZGYuY2x1c3RlcjEwKQp9CmBgYAoKYGBge3J9CmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjAxLmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjAyLmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjAzLmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjA0Lmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjA1Lmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjA2Lmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjA3Lmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjA4Lmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjA5Lmdyb3VwZWQpCmlmICghZW1wdHlfbm9kZXMpIG1wci5kcmF3X21hcChzcGFpbiwgZGYuY2x1c3RlcjEwLmdyb3VwZWQpCmBgYAo=